ContactsProvider2.java revision 9b1bd62417ef1764829398a61c3d5df93a924106
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;
54627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
55568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
563d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
57627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
5867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
593de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.content.SharedPreferences.Editor;
60b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.content.res.AssetFileDescriptor;
611129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikovimport android.database.CharArrayBuffer;
624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
63ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
64a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
65b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.database.sqlite.SQLiteContentHelper;
66b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
69c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
716ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
72d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.os.MemoryFile;
73b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
740e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
75d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.pim.vcard.VCardComposer;
763d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
77508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
783de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
793de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.LiveFolders;
803de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.OpenableColumns;
813de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.SyncStateContract;
82b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
833de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
843de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
853de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
863de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
873de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
883de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
8982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
91ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
92ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
96de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
97b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
984097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
9967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
100a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
101a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
102f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Token;
103f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Tokenizer;
104c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
106d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
107b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
108d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
109d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
110f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport java.lang.ref.SoftReference;
1117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
112315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikovimport java.util.BitSet;
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
1599b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori    private static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
1609b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
1619b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1629b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
1639b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
1649b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori    private static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
1659b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
1669b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1679b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
1689b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
169d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
170d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
1735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_DATA = 1004;
1745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
1785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_PHOTO = 1009;
179f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
1804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
1825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
1835ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
18446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
1854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1866bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
188ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
18948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
19048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
19148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
19248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
19348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
19448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
19548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
19648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
197a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
200b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
201b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
202b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
20382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
20482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
20631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
20731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
208eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
209eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
210ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
211ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
212ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
213ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
21435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
215b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
21635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
217c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
218c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
219c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2201b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2211b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2221b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2231b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2241b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
22546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
22646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
227d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
228f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
229f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
230f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
23167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
23267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2336cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
235f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
236ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
237ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
238d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
23967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
240d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
241ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
24314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
24467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
24688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
2473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2495ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
2503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
251f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
25288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
25388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
25488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
25588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
25688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
25788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
25888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
259f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
2603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
26214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
2633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2645ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
2653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
266f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
2673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
26914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
270321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
27120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
27220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
273321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
274321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
27520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
27620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
277f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
27819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private interface RawContactsQuery {
27919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
28019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
28119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
282ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
283ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
284ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
28519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
28619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
28719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
288ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
289ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
29019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
29119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
292c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
293df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
294caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
29571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
29671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
29771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
29871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
29971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
30071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
30171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
30271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
30371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
30471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
30571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
30671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
30771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
30871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
309a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
310a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
311a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
312a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
313a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
314a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
315a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
316a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
317a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
318a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
319a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
320a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
321038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
322038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    private static final HashMap<String, String> sCountProjectionMap;
323e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
3244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
3255e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
3265e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentStarredProjectionMap;
3275e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentFrequentProjectionMap;
328f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
329f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final HashMap<String, String> sContactsVCardProjectionMap;
330ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
331d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sRawContactsProjectionMap;
33246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    /** Contains the columns from the raw contacts entity view*/
33346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final HashMap<String, String> sRawContactsEntityProjectionMap;
3344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
3354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private static final HashMap<String, String> sDataProjectionMap;
3365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
3375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private static final HashMap<String, String> sDistinctDataProjectionMap;
3389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
339e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    private static final HashMap<String, String> sPhoneLookupProjectionMap;
340ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
341ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
342ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
343ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
344373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
345b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
346eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
347eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final HashMap<String, String> sSettingsProjectionMap;
34882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
34982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final HashMap<String, String> sStatusUpdatesProjectionMap;
3501b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
3511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final HashMap<String, String> sLiveFoldersProjectionMap;
3527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
3549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
3559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
3569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
3579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
3589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
359c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
360c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
3613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
362c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
363ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    /** Precompiled sql statement for incrementing times contacted for a contact */
364ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    private SQLiteStatement mContactsLastTimeContactedUpdate;
3653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
36625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
36782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Precompiled sql statement for updating an aggregated status update */
368a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mLastStatusUpdate;
369f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
370f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupDelete;
371a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateAutoTimestamp;
372a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateInsert;
373a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateReplace;
3740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private SQLiteStatement mStatusAttributionUpdate;
375a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateDelete;
376a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
377f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdEmail;
378f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdIm;
3791129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdStructuredName;
3801129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdOrganization;
3811129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdNickname;
3821129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdPhone;
383f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
3841129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
3851129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
3861129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs3 = new String[3];
387f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
388f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
3904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
391a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
392d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
393d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
394d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
3953653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
3963653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3972d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
3982d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3993653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO);
4005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
4015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
4025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
403f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
4045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
405ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
406ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
4075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
4083653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
4105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
4115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
41246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
41346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
41446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
415b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
4164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
4174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
418ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
41948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
4205e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
421ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
4224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
42348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
4245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
4255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
4264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
427ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
42848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
4291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
430ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
431ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
432ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
433ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
43435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
435b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
436b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
43735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
438a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
439b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
440b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
441b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
442b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
4434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
444eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
445eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
44682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
44782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
4481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
449c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
450c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
451c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
452c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
453c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/#",
454c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
455c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4561b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
4571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
4581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
4591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
4601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
4611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
4621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
4631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
46419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
46519a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
46619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    static {
467038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap = new HashMap<String, String>();
468038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
469e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap = new HashMap<String, String>();
4714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts._ID, Contacts._ID);
4724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
4734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
4744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
4754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
4764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
4774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
4784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
479f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        sContactsProjectionMap.put(Contacts.HAS_PHONE_NUMBER, Contacts.HAS_PHONE_NUMBER);
4804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
4815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sContactsProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
482f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
4833296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
4843296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_PRESENCE,
4853296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
4863296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS,
4873296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
4883296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
4893296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
4903296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
4913296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
4923296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_LABEL,
4933296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
4943296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_ICON,
4953296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
4963296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
4975e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
4985e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
4995e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Long.MAX_VALUE + " AS " + TIMES_CONTACED_SORT_COLUMN);
5005e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5015e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
5025e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
5035e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Contacts.TIMES_CONTACTED + " AS " + TIMES_CONTACED_SORT_COLUMN);
5045e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
505f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap = Maps.newHashMap();
506f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME
507d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                + " || '.vcf' AS " + OpenableColumns.DISPLAY_NAME);
508f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.SIZE, "0 AS " + OpenableColumns.SIZE);
5094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap = new HashMap<String, String>();
5114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
5124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
5134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
5184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DELETED, RawContacts.DELETED);
5194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.TIMES_CONTACTED, RawContacts.TIMES_CONTACTED);
5204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.LAST_TIME_CONTACTED,
5214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.LAST_TIME_CONTACTED);
5224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CUSTOM_RINGTONE, RawContacts.CUSTOM_RINGTONE);
5234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SEND_TO_VOICEMAIL, RawContacts.SEND_TO_VOICEMAIL);
5244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.STARRED, RawContacts.STARRED);
5254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
5264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC1, RawContacts.SYNC1);
5274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC2, RawContacts.SYNC2);
5284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC3, RawContacts.SYNC3);
5294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC4, RawContacts.SYNC4);
5302815f58f72f109790585931f601a63ddc02536a5Evan Millar
5314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap = new HashMap<String, String>();
5324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data._ID, Data._ID);
5334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
5344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
5354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
5364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
5374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
5384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
5394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA1, Data.DATA1);
5404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA2, Data.DATA2);
5414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA3, Data.DATA3);
5424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA4, Data.DATA4);
5434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA5, Data.DATA5);
5444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA6, Data.DATA6);
5454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA7, Data.DATA7);
5464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA8, Data.DATA8);
5474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA9, Data.DATA9);
5484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA10, Data.DATA10);
5494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA11, Data.DATA11);
5504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA12, Data.DATA12);
5514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA13, Data.DATA13);
5524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA14, Data.DATA14);
5534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA15, Data.DATA15);
5544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
5554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
5564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
5574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
55882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sDataProjectionMap.put(Data.CONTACT_ID, Data.CONTACT_ID);
5594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
56456d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
5654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
5664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
5674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
5684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
5694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
5704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
5714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
572a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
5734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
574a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
57546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        HashMap<String, String> columns;
57646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns = new HashMap<String, String>();
57746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts._ID, RawContacts._ID);
57846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
57946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
58046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
58146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
58246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.VERSION, RawContacts.VERSION);
58346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DIRTY, RawContacts.DIRTY);
58446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DELETED, RawContacts.DELETED);
585bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey        columns.put(RawContacts.IS_RESTRICTED, RawContacts.IS_RESTRICTED);
58646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC1, RawContacts.SYNC1);
58746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC2, RawContacts.SYNC2);
58846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC3, RawContacts.SYNC3);
58946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC4, RawContacts.SYNC4);
59046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
59146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.MIMETYPE, Data.MIMETYPE);
59246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA1, Data.DATA1);
59346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA2, Data.DATA2);
59446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA3, Data.DATA3);
59546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA4, Data.DATA4);
59646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA5, Data.DATA5);
59746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA6, Data.DATA6);
59846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA7, Data.DATA7);
59946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA8, Data.DATA8);
60046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA9, Data.DATA9);
60146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA10, Data.DATA10);
60246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA11, Data.DATA11);
60346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA12, Data.DATA12);
60446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA13, Data.DATA13);
60546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA14, Data.DATA14);
60646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA15, Data.DATA15);
60746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC1, Data.SYNC1);
60846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC2, Data.SYNC2);
60946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC3, Data.SYNC3);
61046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC4, Data.SYNC4);
61146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.Entity.DATA_ID, RawContacts.Entity.DATA_ID);
61246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.STARRED, Data.STARRED);
61346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
61446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
61546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
61646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
61746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        sRawContactsEntityProjectionMap = columns;
61846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
6193296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
6203296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_PRESENCE,
6213296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
6223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS,
6233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
6243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
6253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6263296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
6273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
6293296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6303296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
6313296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
6323296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6333296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
6343296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.PRESENCE,
6353296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
6363296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS,
6373296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
6383296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_TIMESTAMP,
6393296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6403296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_RES_PACKAGE,
6413296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6423296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_LABEL,
6433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6443296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_ICON,
6453296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
6463296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        // Projection map for data grouped by contact (not raw contact) and some data field(s)
6485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap = new HashMap<String, String>();
6495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data._ID,
6505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                "MIN(" + Data._ID + ") AS " + Data._ID);
6515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
6525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
6535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
6545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
6555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
6565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA1, Data.DATA1);
6575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA2, Data.DATA2);
6585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA3, Data.DATA3);
6595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA4, Data.DATA4);
6605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA5, Data.DATA5);
6615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA6, Data.DATA6);
6625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA7, Data.DATA7);
6635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA8, Data.DATA8);
6645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA9, Data.DATA9);
6655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA10, Data.DATA10);
6665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA11, Data.DATA11);
6675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA12, Data.DATA12);
6685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA13, Data.DATA13);
6695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA14, Data.DATA14);
6705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA15, Data.DATA15);
6715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
6725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
6735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
6745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
6755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
6768f1631f8a610e7278526916ce73ac1e422a5c9b8Jeff Sharkey        sDistinctDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
6775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
6785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
6795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
6805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
6815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
6825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
6835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
684a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
6855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID,
6865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID);
6875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
6883296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
6893296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_PRESENCE,
6903296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
6913296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS,
6923296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
6933296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
6943296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6953296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
6963296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6973296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
6983296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6993296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
7003296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
7013296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
7023296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
7033296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.PRESENCE,
7043296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
7053296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS,
7063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
7073296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_TIMESTAMP,
7083296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_RES_PACKAGE,
7103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_LABEL,
7123296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_ICON,
7143296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
7153296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
716e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap = new HashMap<String, String>();
717e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
718fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts._ID
719fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup._ID);
72056d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sPhoneLookupProjectionMap.put(PhoneLookup.LOOKUP_KEY,
721fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.LOOKUP_KEY
722fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.LOOKUP_KEY);
723e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
724fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.DISPLAY_NAME
725fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.DISPLAY_NAME);
726e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
727fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.LAST_TIME_CONTACTED
728e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
729e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
730fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.TIMES_CONTACTED
731fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.TIMES_CONTACTED);
732e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
733fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.STARRED
734fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.STARRED);
735e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
736fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.IN_VISIBLE_GROUP
737fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
738e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
739fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.PHOTO_ID
740fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.PHOTO_ID);
741e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
742fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.CUSTOM_RINGTONE
743fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.CUSTOM_RINGTONE);
744e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
745fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.HAS_PHONE_NUMBER
746fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
747e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
748fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.SEND_TO_VOICEMAIL
749e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
750e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
751e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
752e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
753e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.TYPE + " AS " + PhoneLookup.TYPE);
754e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
755e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.LABEL + " AS " + PhoneLookup.LABEL);
7569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
757ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
758ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
75989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups._ID, Groups._ID);
760035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
761035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
7629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
7639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
7649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
76589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.RES_PACKAGE, Groups.RES_PACKAGE);
766ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
76767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
768ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
7693cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
77094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        columns.put(Groups.DELETED, Groups.DELETED);
7713cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
77238446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey        columns.put(Groups.SHOULD_SYNC, Groups.SHOULD_SYNC);
77389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC1, Groups.SYNC1);
77489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC2, Groups.SYNC2);
77589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC3, Groups.SYNC3);
77689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC4, Groups.SYNC4);
777ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
778ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
7796cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts and groups projection map
780ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
781ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
782d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
783d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
784ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
785ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
786ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
787d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
788d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
789ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
790f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov                + " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
791ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
792ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
793b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
794b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
795b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
796b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
7970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID1);
7980c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID2, AggregationExceptions.RAW_CONTACT_ID2);
799b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
800b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
801eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        // Settings projection map
802eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns = new HashMap<String, String>();
803eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
804eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
805eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
806eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
807341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey        columns.put(Settings.ANY_UNSYNCED, "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
808341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + ",(SELECT (CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL THEN 1 ELSE MIN("
809341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Groups.SHOULD_SYNC + ") END) FROM " + Tables.GROUPS + " WHERE "
810fc4e892529eccdfa42121f0304ec7d0dbb42d6c9Dmitri Plotnikov                + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME
811341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
812341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0 THEN 1 ELSE 0 END) AS "
813341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Settings.ANY_UNSYNCED);
81468936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
81568936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " GROUP BY "
81668936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID + " HAVING " + Clauses.HAVING_NO_GROUPS
81768936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + ")) AS " + Settings.UNGROUPED_COUNT);
81868936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
819e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
82068936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Contacts.HAS_PHONE_NUMBER + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
82168936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + " HAVING " + Clauses.HAVING_NO_GROUPS + ")) AS "
82268936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Settings.UNGROUPED_WITH_PHONES);
823eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        sSettingsProjectionMap = columns;
824eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
825373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns = new HashMap<String, String>();
8264dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(PresenceColumns.RAW_CONTACT_ID, PresenceColumns.RAW_CONTACT_ID);
8270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.DATA_ID,
8280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DataColumns.CONCRETE_ID + " AS " + StatusUpdates.DATA_ID);
82982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_ACCOUNT, StatusUpdates.IM_ACCOUNT);
83082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_HANDLE, StatusUpdates.IM_HANDLE);
83182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PROTOCOL, StatusUpdates.PROTOCOL);
83270c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // We cannot allow a null in the custom protocol field, because SQLite3 does not
83370c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // properly enforce uniqueness of null values
83482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.CUSTOM_PROTOCOL, "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL
83582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + "='' THEN NULL ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END) AS "
83682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + StatusUpdates.CUSTOM_PROTOCOL);
83782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PRESENCE, StatusUpdates.PRESENCE);
8380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS, StatusUpdates.STATUS);
8390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_TIMESTAMP, StatusUpdates.STATUS_TIMESTAMP);
8400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_RES_PACKAGE, StatusUpdates.STATUS_RES_PACKAGE);
8410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_ICON, StatusUpdates.STATUS_ICON);
8420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_LABEL, StatusUpdates.STATUS_LABEL);
84382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sStatusUpdatesProjectionMap = columns;
84419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
8451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // Live folder projection
8461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap = new HashMap<String, String>();
8471b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders._ID,
8481b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts._ID + " AS " + LiveFolders._ID);
8491b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders.NAME,
8501b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts.DISPLAY_NAME + " AS " + LiveFolders.NAME);
8511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
8521b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // TODO: Put contact photo back when we have a way to display a default icon
8531b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // for contacts without a photo
8541b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP,
8551b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        //      Photos.DATA + " AS " + LiveFolders.ICON_BITMAP);
8564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
8574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
8583296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    private static void addProjection(HashMap<String, String> map, String toField, String fromField) {
8593296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        map.put(toField, fromField + " AS " + toField);
8603296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    }
8613296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
8623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
8633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
8643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
8653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
8663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
868653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
8693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8701129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        @SuppressWarnings("all")
8713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
8723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
873a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
874a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
875a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
876a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
877a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
878a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
879a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
880a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
8813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
883653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
884653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
885b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
886653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
887653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
888653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
889653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
8903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
8913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
8923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
8935ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
894e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
895e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
896e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
897e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
898653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
899e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
900e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
901e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
9023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
9053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
9063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
9073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
908653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
909f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
91014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
91114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
912653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
913653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
914653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
915653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
916653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
917653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
918653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
919653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
920653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
921653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
922653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
923653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
924653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
925653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
926653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
927653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
928653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
9294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
9304da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " =?", mSelectionArgs1);
931653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
932653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
933f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
934653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
935653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
9363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
93914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
94014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
94114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
9424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
9434da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=?", mSelectionArgs1);
9444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
9454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
9463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
9475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
9483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
9503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9525ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
9534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            long mimeTypeId = getMimeTypeId();
954e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
955e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
9564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
9574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            Cursor c = db.query(DataDeleteQuery.TABLE,
9584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    DataDeleteQuery.CONCRETE_COLUMNS,
9594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=?" +
9604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        " AND " + DataColumns.MIMETYPE_ID + "=" + mimeTypeId,
9614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
9623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
963e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
96414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
965f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
966e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
967e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
968e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
969e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
9703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
9713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
9723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
9733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            if (primaryId != -1) {
9754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                setIsPrimary(rawContactId, primaryId, mimeTypeId);
9764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            }
977e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
978e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
979e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
980e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
981e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
982e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
983e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
984e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
9853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
98725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
988285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
989d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                updateRawContactDisplayName(db, rawContactId);
990fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(db, rawContactId);
991285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
9923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
993a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
994a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
995a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return true;
996a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
997622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
998622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
999622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
1000622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * as baseline, but augmented with any updates.
1001622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1002622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
1003622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
1004622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
10054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
10064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=?",
10074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
1008622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
1009622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
1010622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
1011622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
1012622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        values.put(key, cursor.getString(i));
1013622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
1014622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
1015622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
1016622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1017622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1018622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1019622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1020622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
10213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
10243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
10263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
10273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
1031622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
10323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1033622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
10343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1035622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
10363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
10395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1040622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
104114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
104214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
104314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1044f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1045f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name);
104625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
104714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
104814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
104914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
105014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
105114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1052f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1053622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1054622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1055cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1056622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1057622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
105814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1059f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
106014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1061f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME)) {
1062f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name = values.getAsString(StructuredName.DISPLAY_NAME);
1063f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
1064f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name);
106514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
106625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
106714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
106814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
106914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
107014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
107114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
107214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
107314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
107414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
107514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1076f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
107725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
107814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
10793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1082622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
10833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1084622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1085622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1086622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1087622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
10883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1089622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1090622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1091622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1092622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1093622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1094622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1095622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
109667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1097622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
109867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
109967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1100622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1101622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
11028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1103622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1104622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
110567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
110667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
110767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
110867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
110967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
111067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
11118c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1112622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
1113622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(name);
1114622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
1115622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1116622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1117622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1118622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1119622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1120622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1121622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1122622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1123622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1124622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1125622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1126622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1127622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1128622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1129622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1130622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1131622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1132622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1133622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1134622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1135f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1136622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1137622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1138622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1139f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1140622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1141622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1142622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1143622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1144622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1145622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1146622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1147622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1148622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1149622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1150622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1151622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1152622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1153622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1154622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1155622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1156622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1157622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
115867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
115967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
116067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
116167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1162622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1163622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1164622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1165622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1166622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1167622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
116867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
116967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
117067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1171622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1172622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1173622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
11743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
11753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
11793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
11813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
11823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
11843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
11853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
11863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
11873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
11905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1191622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1192622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1193622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
11943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1195622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1196622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1197f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1198622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1199622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1200622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1201f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1202622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
12033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1204622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1205622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1206622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1207622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1208622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1209622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1210622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1211622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
12123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1213622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1214622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1215622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1216622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1217622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
12183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
12223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
12243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
12253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12285ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1229a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1230a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1231a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1232a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1233a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
123425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1235a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1236a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
12373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
124014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1241f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1242a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1243a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1244a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
124514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
124614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1247f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
124814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
124925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1250a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
1251a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
125214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
125314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
125414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
125514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1256a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
125714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
125814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
125914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
126025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1261a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
126214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
126314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
126414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
126514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
12663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
12673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
12683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
12693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
12703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
12713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
12723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
12733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1274a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1275a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1276a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1277a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1278a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
12793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1281e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1282e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1283e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1284e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1285e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1286e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1287e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
12885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
128914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
129014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
129114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
129214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
129325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1294f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
129514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
129614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
129714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
129814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
129914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1300f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
130114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
130214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
130314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
130414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1305f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
130614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1307f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1308f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
130925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
131014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
131114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
131214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
131314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
131414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
131514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
131614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
131714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
131814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1319f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
132025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
132114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1322e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1323e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1324e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1325e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1326e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1327e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1328e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1329e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1330e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1331e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1332e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1333e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1334e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1335e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
133614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
133714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
133814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
133914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
134014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
134114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
134214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
134314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
134414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
134514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
134614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
134714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
134825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1349f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
135014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
135114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
135214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
135314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
135414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1355f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
135614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
135714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
135814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
135914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1360f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
136114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1362f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1363f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
136425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
136514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
136614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
136714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
136814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
136914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
137014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
137114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
137214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
137314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1374f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
137525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
137614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
137714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
137814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
137914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
13803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
13813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
13833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
13843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
13880b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
13890b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
13900b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
13910b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1392653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
13930b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1394653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
13950b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1396285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
139725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
13980b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
13990b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
14000b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1401653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1402653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1403653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1404653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1405653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1406f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
140714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
140814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
14090b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
14100b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
14110b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1412653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1413f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
1414653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
14150b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1416285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
141725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
14180b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
1419f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
14200b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
142114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
142214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
142314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
142414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
142514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
142614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
142714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
142814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
142914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
143014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1431285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
143225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
143314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1434653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1435653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1436653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private String computeNormalizedNumber(String number, ContentValues values) {
1437e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
1438e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1439e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1440e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1441653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1442653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return normalizedNumber;
1443653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1444e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1445653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1446653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                String number, String normalizedNumber) {
1447e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1448653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
14495ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1450653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1451e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
145236045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.MIN_MATCH,
145336045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                        PhoneNumberUtils.toCallerIDMinMatch(number));
145436045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov
1455653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1456653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else {
14574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
14584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=?", mSelectionArgs1);
1459e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
14603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
14633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
14643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
14653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
14663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
14673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
14683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
14693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
14703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
14713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
14723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
14733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
14743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
14753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
14773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1478653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1479653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1480653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1481653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1482653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1483653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1484653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1485653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1486653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
14870be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
14880be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
14890be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1490653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1491653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1492653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1493653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1494f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
149514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1496653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1497f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
14980be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
14990be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
15000be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
15010be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
15020be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
15030be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
15040be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
15050be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
15060be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
15070be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
15080be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
15090be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
1510b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
15110be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
1512b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
15130be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1514653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1515653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1516653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1517653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1518653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1519653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1520653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1521653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1522653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1523653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1524653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1525653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1526653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1527653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1528653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1529653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1530653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1531653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1532653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1533653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1534653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1535653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1536653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1537653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1538ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
1539ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        mInsertedRawContacts.get(rawContactId));
1540653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1541653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1542653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1543653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1544a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1545a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1546a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1547a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1548a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1549653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1550653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1551a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1552a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1553a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1554a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1555a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1556a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1557a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1558a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1559a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1560285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1561285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1562285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1563a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1564a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1565a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1566a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1567a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1568f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1569a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1570f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1571a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1572a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1573a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1574a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1575a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1576a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1577a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1578a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1579a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1580a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1581a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1582a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1583a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1584a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1585a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1586a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1587a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1588ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    /**
1589ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1590ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1591ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1592ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    public class GroupIdCacheEntry {
1593ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1594ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1595ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1596ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1597ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1598a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
15993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1600b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
160131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
16024097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1603f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1604315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1605315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    // We will use this much memory (in bits) to optimize the nickname cluster lookup
1606315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private static final int NICKNAME_BLOOM_FILTER_SIZE = 0x1FFF;   // =long[128]
1607315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private BitSet mNicknameBloomFilter;
1608315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1609ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<String, SoftReference<String[]>> mNicknameClusterCache = Maps.newHashMap();
1610ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
1611622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1612622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1613ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1614ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1615ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // is a list of groups with this group id.
1616ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1617ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
1618622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1619f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1620a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1621a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
162220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
16231129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private CharArrayBuffer mCharArrayBuffer = new CharArrayBuffer(128);
162420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1625ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
162673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1627ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<Long, Account> mInsertedRawContacts = Maps.newHashMap();
1628b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1629a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private HashSet<Long> mDirtyRawContacts = Sets.newHashSet();
1630b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1631de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
16321a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
16331a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
163481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
163581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
16364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
16374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1638de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1639ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1640ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1641ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1642ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1643ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1644ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1645ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
164635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1647ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
1648de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
1649b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
1650a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1651b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1652d076a108d58b30591f197e1b90fa8de60999c499Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper);
16530e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1654a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1655b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
1656653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1657c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
1658653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1659653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1660653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1661653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1662653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1663c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
1664653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1665653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1666653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1667653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1668653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1669653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1670653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1671653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1672653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1673653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1674653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1675ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate = db.compileStatement(
1676ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                "UPDATE " + Tables.CONTACTS +
1677ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                " SET " + Contacts.LAST_TIME_CONTACTED + "=? " +
1678ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                "WHERE " + Contacts._ID + "=?");
1679a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
168025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate = db.compileStatement(
168125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
168225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " SET " + RawContactsColumns.DISPLAY_NAME + "=?,"
168325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        + RawContactsColumns.DISPLAY_NAME_SOURCE + "=?" +
168425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
16853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1686a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mLastStatusUpdate = db.compileStatement(
1687a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                "UPDATE " + Tables.CONTACTS +
1688a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1689a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "(SELECT " + DataColumns.CONCRETE_ID +
1690a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " FROM " + Tables.STATUS_UPDATES +
1691a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.DATA +
1692a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1693a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1694a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
1695a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1696a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1697a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
16980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
16990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                                + StatusUpdates.STATUS +
1700a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                        " LIMIT 1)" +
1701a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1702e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1703622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        final Locale locale = Locale.getDefault();
170428f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
170528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
170628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
170728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
1708622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                context.getString(com.android.internal.R.string.common_name_conjunctions),
1709622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                locale);
1710f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
1711622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mPostalSplitter = new PostalSplitter(locale);
17124097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
1713f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert = db.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1714f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1715f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1716f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1717f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete = db.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1718f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1719f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1720a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateInsert = db.compileStatement(
1721a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1722a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
17230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
17240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
17250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
17260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
17270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?)");
1728a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1729a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateReplace = db.compileStatement(
1730a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1731a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
17320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
17330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
17340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
17350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
17360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
17370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1738a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1739a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateAutoTimestamp = db.compileStatement(
1740a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
17410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
17420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
1743a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
17440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
17450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
17460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        mStatusAttributionUpdate = db.compileStatement(
17470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
17480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
17490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
17500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
17510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1752a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1753a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateDelete = db.compileStatement(
1754a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1755a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1756a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
17573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
17583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1759e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
17603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
17613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
176267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
176367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
17643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
17653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
176614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
17673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
17683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
1769622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
1770622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                new StructuredPostalRowHandler(mPostalSplitter));
1771a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
1772a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
17733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
17743d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
1775568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            importLegacyContactsAsync();
17763d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1777568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1778c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        verifyAccounts();
177970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
1780f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1781f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
17821129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdStructuredName = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE);
17831129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdOrganization = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE);
17841129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdNickname = mDbHelper.getMimeTypeId(Nickname.CONTENT_ITEM_TYPE);
17851129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdPhone = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
1786315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        preloadNicknameBloomFilter();
17871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
17884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
17894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1790c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
1791c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
1792c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
1793c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
1794c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
179531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1796de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1797b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1798b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
179931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
180031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1801013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1802013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1803013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1804013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
18053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
18063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        return prefs.getInt(PREF_CONTACTS_IMPORTED, 0) < PREF_CONTACTS_IMPORT_VERSION;
18083d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18093d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1810568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1811568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1812568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1813568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1814568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1815568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
1816568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
1817568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1818568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
1819ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        mAccessLatch = new CountDownLatch(1);
1820568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1821568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
1822568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
1823568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
1824568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                if (importLegacyContacts()) {
1825d076a108d58b30591f197e1b90fa8de60999c499Dmitri Plotnikov                    // TODO aggregate all newly added raw contacts
1826568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1827568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    /*
1828568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * When the import process is done, we can unlock the provider and
1829568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * start aggregating the imported contacts asynchronously.
1830568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     */
1831ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch.countDown();
1832ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1833568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
1834568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
1835568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
1836568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1837568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
1838568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1839568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
18403d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private boolean importLegacyContacts() {
1841568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1842568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        if (importLegacyContacts(importer)) {
18433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            Editor editor = prefs.edit();
18453d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.putInt(PREF_CONTACTS_IMPORTED, PREF_CONTACTS_IMPORT_VERSION);
18463d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.commit();
18473d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
18483d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } else {
18493d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return false;
18503d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
18513d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
18533d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1854568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
18550e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
18563d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
18573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
18583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            importer.importContacts();
18590e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff            mContactAggregator.setEnabled(aggregatorEnabled);
18603d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
18613d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
18623d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
18633d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           return false;
18643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
18653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1867a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1868a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1869a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1870a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1871b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
1872a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1873a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1874568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1875568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
1876568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1877568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1878568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1879568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1880568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1881568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
1882ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
1883ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
1884ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
1885ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
1886ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
1887ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1888ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
1889ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
189081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
1891ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
1892ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1893568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1894568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1895568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1896568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1897568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
1898568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1899568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1900568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1901568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1902568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1903568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
1904568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1905568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1906568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1907568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1908568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1909568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
1910568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1911568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1912568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1913568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1914568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1915568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1916568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
1917568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1918568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1919568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1920568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
19214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1922285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1923bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1924b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1925b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1926285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
19271ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1928b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1929b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1930b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1931b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
1932285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
1933b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
1934df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
1935a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.clear();
1936285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1937285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1938285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1939285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
19401129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1941bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1942b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1943b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1944285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1945b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
19461ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
19471a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
19481a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1949b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
19501a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
1951b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1952b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1953b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1954bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1955b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1956b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
19571129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
195808e42c9c153a60bf2e7c71dd40bf84bb5fc93555Dmitri Plotnikov        for (long rawContactId : mInsertedRawContacts.keySet()) {
1959d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            updateRawContactDisplayName(mDb, rawContactId);
1960d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
1961285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1962b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1963a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        if (!mDirtyRawContacts.isEmpty()) {
1964a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1965a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1966a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mDirtyRawContacts);
1967a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1968a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
1969a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1970a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1971b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
1972a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1973a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1974a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mUpdatedRawContacts);
1975a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1976a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
1977b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1978b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1979b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
1980b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
1981b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().update(mDb, id, entry.getValue());
1982b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1983b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1984b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1985b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1986b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1987a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1988a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1989a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1990a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1991a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private void appendIds(StringBuilder sb, HashSet<Long> ids) {
1992b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1993a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1994b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1995a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1996a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1997285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1998285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1999285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2000cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
200181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
200281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
200381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
200481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
200581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
200681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
200781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2008cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2009568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2010285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
2011ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return mInsertedRawContacts.containsKey(rawContactId);
2012285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2013285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
20143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
20153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
20163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
20173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
20183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
20193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
20203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
20213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
20223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
20234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2024de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2025bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
20261129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2027b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2028f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2029f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2030f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2031f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2032a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2033a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
203435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2035a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
203635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2037b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
203835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
203935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2040d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2041d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
20426bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
20436bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
20446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
20455ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2046f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertRawContact(uri, values);
2047f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2048a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2049a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2050a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
20515ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
20525ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2053f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2054f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2055a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2056a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2057a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2058a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2059f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2060f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2061a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2062a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2063a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2064ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2065f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2066f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2067ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2068ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2069ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2070eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
20715aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
207243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2073eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2074eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2075eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
207682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
207782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
20781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
20791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
20801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2081a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
208281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2083f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2084a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2085a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
20867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
20877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
20887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
20897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2090de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2091a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2092a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2093a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2094035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
2095035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
2096f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the ContentValues to read from and update
2097f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the explicitly provided Account
2098f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @return false if the parameters are inconsistent
20997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2100f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private boolean resolveAccount(Uri uri, ContentValues values) {
2101f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2102f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2103f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2104f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
2105f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = null;
2106f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = null;
2107f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2108f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2109f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2110f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2111f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2112f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (TextUtils.isEmpty(valueAccountName) && TextUtils.isEmpty(valueAccountType)) {
2113f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2114f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2115f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
2116f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (accountName != null && !accountName.equals(valueAccountName)) {
2117f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return false;
2118f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
2119f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2120f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (accountType != null && !accountType.equals(valueAccountType)) {
2121035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
2122035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
2123f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2124f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2125f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2126f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2127f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2128f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
2129f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = null;
2130f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return true;
2131035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2132f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2133f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2134f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2135f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2136f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2137035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2138f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2139035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
21407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
21417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2143d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
21446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
21456bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
21466bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
21476bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2148d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2149de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
21506bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
21516bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
21526bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2153a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2154a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2155f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2156f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2157a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2158a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2159f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertRawContact(Uri uri, ContentValues values) {
2160f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2161f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2162f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2163f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2164f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (!resolveAccount(uri, mValues)) {
21657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
21667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
21677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
21693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2170f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
21713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
21723d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2173f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2174023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId);
2175285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2176285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2177f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mInsertedRawContacts.put(rawContactId, mAccount);
2178f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2179023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2180a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2181a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2182a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2183a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2184a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2185a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2186a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2187a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2188f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2189a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2190de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2191de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
219267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2193de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
219420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2195de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2196de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2197de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2198b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2199de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2200de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2201508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2202de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2203de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2204de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2205de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2206de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
22074097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2208b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2209de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2210a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2211a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2212a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2213f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2214de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2215a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2216b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2217a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2218a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
2219a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
2220a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2221a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
22224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
22234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
22248e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
22258e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
22268e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
22278e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
22288e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2229b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
2230f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
22318e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
22328e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
22338e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
22348e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
2235421782cb554e5050cf62a86b98df6520038dcd15Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId);
2236f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22378e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
22388e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
22398e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
2240b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
2241f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
22428e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
22438e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
22448e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2245f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22468e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2247f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2248c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
2249b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
22508e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
2251f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22528e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2253f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2254f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2255f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2256a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
22575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
22589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
22599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
22605ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
22619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
22629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
22639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
22649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
22659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
2266ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
2267ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Account account) {
2268ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2269ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (account == null) {
22704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
2271ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
22724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
2273ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            try {
2274ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                if (c.moveToFirst()) {
2275ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
2276ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
2277ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2278ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        account = new Account(accountName, accountType);
2279ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    }
22809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2281ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            } finally {
2282ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                c.close();
22839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
22849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2285ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
22869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
22879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
2288ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    + "has a sourceid the the contact must be associated with "
22899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
22909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
22919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2292ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
2293ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (entries == null) {
2294ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            entries = new ArrayList<GroupIdCacheEntry>(1);
2295ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            mGroupIdCache.put(sourceId, entries);
2296ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2297ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2298ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int count = entries.size();
2299ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        for (int i = 0; i < count; i++) {
2300ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            GroupIdCacheEntry entry = entries.get(i);
2301ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) {
2302ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                return entry.groupId;
2303ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            }
2304ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2305ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2306ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        GroupIdCacheEntry entry = new GroupIdCacheEntry();
2307ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountName = account.name;
2308ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountType = account.type;
2309ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.sourceId = sourceId;
2310ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entries.add(0, entry);
2311ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
23129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
23135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
2314ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
23159261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2316df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
23179261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
2318ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (c.moveToFirst()) {
2319ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = c.getLong(0);
23209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
23219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2322df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2323df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
23249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
23259261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
23269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
23279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
23289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
23299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2330ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = groupId;
23319261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
23329261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
23339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
23349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2335ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2336ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return entry.groupId;
23379261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
23389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2339d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private interface DisplayNameQuery {
23401129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        public static final String RAW_SQL =
23411129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                "SELECT "
23421129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + DataColumns.MIMETYPE_ID + ","
23431129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.IS_PRIMARY + ","
23441129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.DATA1 + ","
23451129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Organization.TITLE +
23461129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " FROM " + Tables.DATA +
23471129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " WHERE " + Data.RAW_CONTACT_ID + "=?" +
23481129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        " AND (" + Data.DATA1 + " NOT NULL OR " +
23491129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                Organization.TITLE + " NOT NULL)";
2350d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2351d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int MIMETYPE = 0;
2352d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int IS_PRIMARY = 1;
2353d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int DATA = 2;
2354d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int TITLE = 3;
2355d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2356d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2357d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    /**
2358d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * Updates a raw contact display name based on data rows, e.g. structured name,
2359d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * organization, email etc.
2360d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     */
2361d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
2362d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        String bestDisplayName = null;
2363d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
2364d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
23651129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(rawContactId);
23661129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        Cursor c = db.rawQuery(DisplayNameQuery.RAW_SQL, mSelectionArgs1);
2367d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        try {
2368d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            while (c.moveToNext()) {
23691129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                int mimeType = c.getInt(DisplayNameQuery.MIMETYPE);
23701129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
23711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                // Display name is at DATA1 in all type. This is ensured in the
23721129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                // constructor.
23731129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                mCharArrayBuffer.sizeCopied = 0;
23741129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                c.copyStringToBuffer(DisplayNameQuery.DATA, mCharArrayBuffer);
23751129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                if (mimeType == mMimeTypeIdOrganization && mCharArrayBuffer.sizeCopied == 0) {
23761129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.TITLE, mCharArrayBuffer);
2377d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
23781129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
23791129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                if (mCharArrayBuffer.sizeCopied != 0) {
23801129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    int source = getDisplayNameSource(mimeType);
23811129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    if (source > bestDisplayNameSource) {
2382d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                        bestDisplayNameSource = source;
23831129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
23841129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
23851129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    } else if (source == bestDisplayNameSource
23861129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            && source != DisplayNameSources.UNDEFINED) {
23871129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        if (mimeType == mMimeTypeIdStructuredName
23881129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                || c.getInt(DisplayNameQuery.IS_PRIMARY) != 0) {
23891129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayNameSource = source;
23901129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayName = new String(mCharArrayBuffer.data, 0,
23911129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                    mCharArrayBuffer.sizeCopied);
23921129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        }
2393d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                    }
2394d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
2395d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            }
2396d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2397d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        } finally {
2398d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            c.close();
2399d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        }
2400d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2401d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        setDisplayName(rawContactId, bestDisplayName, bestDisplayNameSource);
2402d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2403d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
24041129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private int getDisplayNameSource(int mimeTypeId) {
24051129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        if (mimeTypeId == mMimeTypeIdStructuredName) {
24061129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.STRUCTURED_NAME;
24071129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdEmail) {
24081129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.EMAIL;
24091129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdPhone) {
24101129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.PHONE;
24111129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdOrganization) {
24121129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.ORGANIZATION;
24131129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdNickname) {
24141129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.NICKNAME;
24151129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else {
24161129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.UNDEFINED;
24171129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        }
24181129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    }
24191129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
24209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
242120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
242220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2423f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
242420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
242520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2426de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2427de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
242814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2429de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2430de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
243114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
243214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2433a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2434a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
2435f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
243688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
2437a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    if (rowHandler.isAggregationRequired()) {
2438a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                        triggerAggregation(rawContactId);
2439a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    }
244088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
244120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
244220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2443de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
244420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
244520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
244620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
244720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
244820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
244988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
245088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
245188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
245220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2453f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
245488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
245588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
24564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
24574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=?",
24584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2459f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
246020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
246120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
246220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
246320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
246420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
246514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
246620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
246720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
246820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
246920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
247020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
247120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
247220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
247320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
247420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
24757a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
247620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
247720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
247820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2479a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2480a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = rowHandler.delete(mDb, c);
24818e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
2482a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            if (rowHandler.isAggregationRequired()) {
2483a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                triggerAggregation(rawContactId);
2484a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            }
24858e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return count;
248620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
248720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
248820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
248920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
249020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
249120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2492ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2493ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2494f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2495f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2496f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2497f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2498f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (!resolveAccount(uri, mValues)) {
2499ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
2500ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
2501ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2502ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2503f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
250467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2505f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
250667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2507f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2508ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2509f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2510f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
251173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
251273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2513f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2514ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2515f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
25161a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2517ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2518ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2519ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2520ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2521ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
25225aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2523e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
25245aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
25251a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
25261a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2527e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
25281a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2529e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2530e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2531e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2532ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
253382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
25341f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
253582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
253682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
25370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
25384dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
25394dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
25400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
254182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
25424dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
25434dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
25444dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
25454dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
25461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
25471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2548dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2549dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
255082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2551f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
2552dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2553dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2554dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2555f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=");
2556f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            mSb.append(dataId);
25571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2558dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2559dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
25600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
25610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
25620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
25630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2564dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2565dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2566dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2567dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
2568f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2569f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2570f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2571f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2572f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2573f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2574f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
2575f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (")
2576f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdEmail)
2577f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(",")
2578f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2579f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(")" + " AND " + Data.DATA1 + "=");
2580f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(mSb, handle);
2581f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(" AND ((" + DataColumns.MIMETYPE_ID + "=")
2582f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2583f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.PROTOCOL + "=")
2584f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(protocol);
2585dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
2586f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
2587f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
2588dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2589f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=")
2590f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdEmail)
2591f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append("))");
2592dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
2593f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=")
2594f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2595f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.PROTOCOL + "=")
2596f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(protocol)
2597f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.DATA + "=");
2598f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(mSb, handle);
2599dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
2600f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
2601f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
2602dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2603dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
26041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
260582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
2606f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=")
260782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                        .append(values.getAsLong(StatusUpdates.DATA_ID));
2608dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
260970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
2610f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
261170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
26121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
26131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2614de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
2615c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    mSb.toString(), null, null, null,
2616c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    Contacts.IN_VISIBLE_GROUP + " DESC, " + Data.RAW_CONTACT_ID);
26171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
261867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
26195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2620e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
26211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
26221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
26231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
26241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
26251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
262631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
262731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
262831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
26291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
26301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
263182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2632a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2633a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2634a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2635a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2636a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2637a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2638a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
263982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2640a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2641a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
264282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
264382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
264482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
264582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
264682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2647a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
264882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
264982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
26501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2651a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2652a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2653a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2654e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
26550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
265682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
265782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
26580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
26590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
26600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
26620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
26630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
26640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
26650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
26660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
26680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
26690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2670a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
2671a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
2672a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
267382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
267482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
2675a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
2676a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
26770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 3, status);
26780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 4, resPackage);
26790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 5, iconResource);
26800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 6, labelResource);
2681a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
2682a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
2683a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2684a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
2685a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
26860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 2, status);
26870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 3, resPackage);
26880a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 4, iconResource);
26890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 5, labelResource);
2690a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
2691a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
2692a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
26930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    long timestamp = System.currentTimeMillis();
2694a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
26950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 2, status);
2696a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
26970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 4, status);
2698a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
26990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
27000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 1, resPackage);
27010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 2, iconResource);
27020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 3, labelResource);
27030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
27040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.execute();
2705a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
2706e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2707e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2708bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2709a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2710a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
2711a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
2712a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.execute();
2713a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2714a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2715a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
27161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
27171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
27184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2719de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2720bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2721b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2722b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2723b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2724f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2725f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2726508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2727508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
272835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2729b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
273035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2731b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2732b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2733b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2734b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2735b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2736b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2737cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2738cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2739cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2740cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2741cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2742d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2743d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2744cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return deleteContact(contactId);
27456bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
27466bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
27472e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
27482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
27492e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
27502e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
27512e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
27522e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
27532e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
27542e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
27552e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
27562e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                return deleteContact(contactId);
27572e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
27582e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
27592971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
27602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
2761fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
2762fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
2763e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
27642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
27652971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
27662971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2767fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
2768fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
2769fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
27702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
27712971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
27722971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
27732971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
27742971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
27752971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
27762971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
27775ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
27782971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2779fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
2780fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
2781508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2782508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
278320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2784f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2785944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2786f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
278720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
278820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
278948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
279048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
279148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
279248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2793508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2794f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
27954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
27964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
2797ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2798ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2799ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2800f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
28015aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
28022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
28032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
28042971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
28052971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
28062971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2807e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
28082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
28092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
28105aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
28112971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
28122971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
28132971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
28142971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
281581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2816f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
281781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
28182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2819508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2820508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2821eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
282243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
28235aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteSettings(uri, selection, selectionArgs);
2824eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2825eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
282682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
28270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
28281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
28291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
283081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
283181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
28323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
283381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2834508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
28354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
28364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
28371c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
2838ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2839b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
284094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2841de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
284294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
284394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
284494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
284594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2846f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2847de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
284894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
284994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
285094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2851f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2852de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
285394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
285494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
28551a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
285694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
285794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
285894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
28595aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2860e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
28611a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
2862e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2863e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2864e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2865cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int deleteContact(long contactId) {
2866cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2867cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                RawContacts.CONTACT_ID + "=" + contactId, null, null, null, null);
2868cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2869cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2870cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2871cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                markRawContactAsDeleted(rawContactId);
2872cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2873cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2874cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2875cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2876cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2877cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2878cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2879cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2880fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
28813389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
2882f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
288314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2884fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
2885fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
2886fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
288733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2888b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2889cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return markRawContactAsDeleted(rawContactId);
289033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
289133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
289233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
28930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
28949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
28959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
28969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
28979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
28989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
28999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
29009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
29019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
29020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
29030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2904cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int markRawContactAsDeleted(long rawContactId) {
290581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
290681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2907cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2908cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2909cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2910cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2911cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2912cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2913cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return updateRawContact(rawContactId, mValues);
2914cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2915cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
29164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2917de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2918de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2919bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2920b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2921b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2922b5a4add17815167d20a90645779df34cdf45280dFred Quintana
292335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
292400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
292500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2926b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2927b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
29281129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
2929b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
2930b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2931b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2932b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2933f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2934f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
293500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
293635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2937b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2938b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2939b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2940b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2941b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2942b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2943b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2944b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2945b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2946b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2947b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
294835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2949d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
29508c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(values, selection, selectionArgs);
295100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
295200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
295300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2954d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
29558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(ContentUris.parseId(uri), values);
2956c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2957c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2958c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
29592e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
29602e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
29612e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
29622e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
29632e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
29642e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
29652e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
29662e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
29672e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
29688c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(contactId, values);
29692e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
29702e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
29712e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
29727d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
29737d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
29747d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
29757d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
29767d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
29777d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
29787d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
29797d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
29807d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
29817d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
298220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2983944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2984f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
298581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2986f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
298781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
298820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
298920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2990c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
299148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
299248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
299348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
299448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2995f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
299681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2997f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
299881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
299900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
300000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
30017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
30025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
30035ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
30044529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count = updateRawContacts(values, selection, selectionArgs);
30057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
30067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
30077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
30085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
300933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
30104529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
30114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
30124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
30134529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                                    + " AND(" + selection + ")", selectionArgs);
30144529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
30154da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
30164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1);
30174529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
30187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
30197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
30207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3021ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
30225aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3023f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
302481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3025f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
302681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3027ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3028ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3029ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3030ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3031ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
30324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
30334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
303473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
30355aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
30365aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
303781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3038f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
303981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3040ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3041ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3042ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3043127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3044de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3045b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3046b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3047b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3048eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
30495aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateSettings(uri, values, selection, selectionArgs);
305043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3051eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3052eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3053eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
30549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
30559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
30569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
30579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
30589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
305981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
306081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3061f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
306281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
306300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
306400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
306500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
30664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
30674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
30689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
30699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
30709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
30719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
30729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
30739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
30749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
30759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
30769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
30779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
30789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
30799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
30809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
30829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
30839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
30849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
30859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
30869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
30879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
30889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
30899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
30909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
30919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
30939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
30949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
30959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
30969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
30979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
30989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
30999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
31009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
31019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
31029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
31039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
31049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
31059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
31069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
31079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
31089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
31099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
31109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
31119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
31129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
31139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
31149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
31159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
31169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
31179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
31189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
31199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
31209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
31219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
31229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
31239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
31249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
31255aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3126f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
312773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3128ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3129ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
313073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3131f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
313273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
313373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
313473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
313573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
313673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
313773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
313873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
313973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3140ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
31411a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
31421a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
314394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
31446ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
31451129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
31466ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            final long groupId = ContentUris.parseId(uri);
31474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(groupId);
31486ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
31494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Groups.ACCOUNT_TYPE}, Groups._ID + "=?", mSelectionArgs1, null,
31506ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
31516ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
31526ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
31536ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
31546ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
31556ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
31566ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
31576ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
31586ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3159ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
31606ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
31616ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
31626ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
31636ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
31646ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
31656ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
31666ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
31676ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
316894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
316994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
317094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3171b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3172b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3173e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
31741a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
31751a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3176e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3177e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3178e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3179e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
31804529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs) {
31814529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
31824529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
31834529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
31844529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
318573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
31864529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3187b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
318851bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
31894529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
31904529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
31914529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
31924529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
31934529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                updateRawContact(rawContactId, values);
31944529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
31954529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
31964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
31974529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
31984529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
31994529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
32004529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
32014529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
32024529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
32034529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContact(long rawContactId, ContentValues values) {
320419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final String selection = RawContacts._ID + " = " + rawContactId;
320519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
320619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
320719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3208ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3209ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
321019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
321119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
321219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    null, null, null, null);
321319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
321419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
321519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3216ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3217ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
321819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
321919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
322019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
322119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
322219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
322319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
322419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
322519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, null);
32265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3227433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
32284529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3229433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3230285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
3231285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateLookupKey(mDb, rawContactId);
3232285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
323319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
323419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                // undo delete, needs aggregation again.
3235ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                mInsertedRawContacts.put(rawContactId, new Account(accountName, accountType));
323619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
32375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
32385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
323933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
324033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3241321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3242f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
324320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
324420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
324520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
32465ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
324720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
324820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
324920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
325020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
325120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3252b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
325320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
325420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
325570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
325670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
325720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
325820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
325920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
326070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
326120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
326270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
326320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
326470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
326520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
326670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
326720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
326820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3269653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
327020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3271653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3272653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
327314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3274653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3275653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
3276f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
327720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3278653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3279653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
328020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
328120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3282653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
328320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
328420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3285f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3286653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3287653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3288321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3289653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
329014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
3291a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3292f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        rowHandler.update(mDb, values, c, callerIsSyncAdapter);
32938e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
3294a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
3295a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
3296a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
32978e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
3298653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return 1;
3299321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3300321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
33018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
33028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            String[] selectionArgs) {
33038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3304b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
33058c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
33068c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
33078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
33088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
33098c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
33108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                updateContactOptions(contactId, values);
33118c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
33128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
33138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
33148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
33158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
33168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
33178c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
33188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
33198c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
33208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(long contactId, ContentValues values) {
3321d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
33228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3323b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3324d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3325b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3326d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3327b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3328d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3329b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3330d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3331b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3332d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3333d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3334d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
33358c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3336d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3337d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3338d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
33398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3340c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
33418c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3342c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3343c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
33444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
33454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?", mSelectionArgs1);
33468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
33478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
33488c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
33498c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3350b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
33518c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3352b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
33538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3354b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
33558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3356b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
33578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3358b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
33598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
33608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
33619b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
33629b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
33639b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
33649b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
33659b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
33669b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
33679b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
3368f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3369d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3370ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    public void updateContactLastContactedTime(long contactId, long lastTimeContacted) {
3371ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
3372ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.bindLong(2, contactId);
3373ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.execute();
3374d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
3375d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3376127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3377127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
33780c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
33790c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
338080c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
33810c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
33820c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
33830c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
33840c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
33850c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
33860c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
33870c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3388b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3389127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
33900c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
33914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
33924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
33930c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
33944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
33954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
33960c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
33976bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
33986bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
33990c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
34000c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
34010c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
34020c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3403127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3404127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
34053389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
3406dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1);
3407dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2);
3408dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
3409b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId1 = mDbHelper.getContactId(rawContactId1);
34100c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
34110c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
3412b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId2 = mDbHelper.getContactId(rawContactId2);
34130c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
3414127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3415127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3416127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3417127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3418b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3419b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
342070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
3421b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
342270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        if (mDb == null) return;
342370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
3424627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
3425627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        boolean hasUnassignedContacts[] = new boolean[]{false};
342670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
342770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
3428627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3429627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.RAW_CONTACTS, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE);
3430627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3431627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.GROUPS, Groups.ACCOUNT_NAME, Groups.ACCOUNT_TYPE);
3432627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3433627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.SETTINGS, Settings.ACCOUNT_NAME, Settings.ACCOUNT_TYPE);
343448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3435627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
3436627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // in the existingAccounts set will be extra accounts whose data must be deleted.
3437627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
3438627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
3439627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
344070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
344170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
344270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            for (Account account : accountsToDelete) {
34435f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Log.d(TAG, "removing data for removed account " + account);
3444627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                String[] params = new String[] {account.name, account.type};
3445627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3446627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.GROUPS +
3447627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
3448627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
3449627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3450627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.PRESENCE +
3451627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
3452627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                "SELECT " + RawContacts._ID +
3453627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
3454627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3455627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
3456627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3457627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.RAW_CONTACTS +
3458627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3459627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
3460627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3461627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.SETTINGS +
3462627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
3463627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
3464627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3465627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3466627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            if (hasUnassignedContacts[0]) {
3467627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3468627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                Account primaryAccount = null;
3469627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                for (Account account : accounts) {
3470627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    if (isWritableAccount(account)) {
3471627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        primaryAccount = account;
3472627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        break;
3473627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    }
3474627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3475627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3476627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (primaryAccount != null) {
3477627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    String[] params = new String[] {primaryAccount.name, primaryAccount.type};
3478627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3479627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
3480627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.RAW_CONTACTS +
3481627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + RawContacts.ACCOUNT_NAME + "=?,"
3482627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + RawContacts.ACCOUNT_TYPE + "=?" +
3483627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " IS NULL" +
3484627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL", params);
3485627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3486627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // We don't currently support groups for unsynced accounts, so this is for
3487627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // the future
3488627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
3489627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.GROUPS +
3490627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + Groups.ACCOUNT_NAME + "=?,"
3491627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + Groups.ACCOUNT_TYPE + "=?" +
3492627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " IS NULL" +
3493627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + Groups.ACCOUNT_TYPE + " IS NULL", params);
3494627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
349570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
3496627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3497b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
349870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
349970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
350070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
350170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
350270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3503619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3504619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3505627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
3506627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
3507627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    private void findValidAccounts(Set<Account> validAccounts, boolean[] hasUnassignedContacts,
3508627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            String table, String accountNameColumn, String accountTypeColumn) {
3509627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        Cursor c = mDb.rawQuery("SELECT DISTINCT " + accountNameColumn + "," + accountTypeColumn
3510627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                + " FROM " + table, null);
3511627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
3512627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
3513627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (c.isNull(0) && c.isNull(1)) {
3514627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    hasUnassignedContacts[0] = true;
3515627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                } else {
3516627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
3517627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3518627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3519627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
3520627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
3521627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
3522627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
3523627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3524627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    /**
3525622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
3526622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
352767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
352867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
352967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
353067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
353167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
353267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
353367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
353467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
353567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
353667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
353767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
353867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
3539dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
3540622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
3541dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
3542dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
3543622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
3544622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
3545dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
3546622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
3547622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
35484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
35494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
35504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
3551bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3552bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
3553bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
35540b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
3555b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
355635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3557d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
35581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
3559c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
3560c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3561619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
3562619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
3563a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
35644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
356535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3566b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
356735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
356835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3569d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3570763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3571619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
3572619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
3573619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3574d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
35754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3576763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
35774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
35784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
35796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
35806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
35816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
35825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
35835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
35845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
35855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
35865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
35875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
35885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
35895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
35905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
35915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
35925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3593763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
35944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    String[] args;
35954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    if (selectionArgs == null) {
35964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        args = new String[2];
35974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    } else {
35984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        args = new String[selectionArgs.length + 2];
35994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
36004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    }
36014da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    args[0] = String.valueOf(contactId);
36024da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    args[1] = lookupKey;
36034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
36044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
36055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            groupBy, limit);
36065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (c.getCount() != 0) {
36075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
36085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
36095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    c.close();
36115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
36125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3613763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
36144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
36154da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
36164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
36175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
36185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
36195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3620f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
3621f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
3622f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
3623763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
3624f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
36254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
36264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
36274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
3628f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
3629f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
3630f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
3631ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
3632763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3633ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
36344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
36354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3636e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
36375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
36384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(sb.toString());
3639ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
3640ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3641ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3642ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3643ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
3644ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
36454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3646ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3647d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
36484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
36494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3650e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
36515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
36524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
36534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
36544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3655763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3656ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
36575e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
36585e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
36595e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
36605e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    starredProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
36615e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    frequentProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
36625e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
36635e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
36644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
36654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
36664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3667d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
36685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
36695e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
36704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3671d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3672d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3673d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3674763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
36754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
36764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3677d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
36785e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
36795e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
3680d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3681d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
36824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3683d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3684d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3685d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3686d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
36874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
36884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3689d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3690d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3691d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3692d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3693d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3694d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3695ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3696763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3697b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
369871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
36994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3700b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3701b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3702b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3703b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
3704d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_DATA: {
37054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
370682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
37084da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
37096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
37106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
371100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3712ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_PHOTO: {
37133653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
371482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37154da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
37164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
37173653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
37183653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
37193653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
37203653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
37214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
372282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
372389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
37242815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
37252815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
37262815f58f72f109790585931f601a63ddc02536a5Evan Millar
372748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
372882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
373048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
37314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
373248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
373348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
373448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3735ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
373682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
373789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3738ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
37394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
37404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3741a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
37425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
37435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
37445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
37455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
37465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
37477318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
37485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
37495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
37505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
37515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (isPhoneNumber(filterParam)) {
37525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
37535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
37545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
37555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
37565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
37575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
37585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
37595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " FROM " + Tables.PHONE_LOOKUP
37605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
37615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(reversed);
37625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append("')");
37635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
37645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3765a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
3766ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
37675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3768a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3769a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3770a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
3771ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3772ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3773ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
37744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
377582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
377689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
37774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
37784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
37794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
378048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
378182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
37834da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
37844da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
378548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
378648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
378748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
37885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
378982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
379089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
37914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
37924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
37934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    qb.appendWhere(" AND " + Email.DATA + "=?");
37944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3795ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3796ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3797ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
37985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
379982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
380007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
380107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
380207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
380307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
380407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
380507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
380607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
38075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
380807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
380907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
381007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
381107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
381207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
381307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
381407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
381507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
381607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
381707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
381807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " AND " + Data.DATA1 + " LIKE ");
381907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
382020938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
382120938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
382220938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
382307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
382407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            /*
382507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * Using a UNION instead of an "OR" to make SQLite use the right
382607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * indexes. We need it to use the (mimetype,data1) index for the
382707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * email lookup (see above), but not for the name lookup.
382807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * SQLite is not smart enough to use the index on one side of an OR
382907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * but not on the other. Using two separate nested queries
383007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * and a UNION between them does the job.
383107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             */
383207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            sb.append(
383307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " UNION SELECT " + Data._ID +
383407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " FROM " + Tables.DATA +
383507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " WHERE +" + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
383607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " AND " + Data.RAW_CONTACT_ID + " IN ");
38377318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
383820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
38395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
38405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3841a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
38425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
38435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
3844a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3845a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3846a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
38475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
38485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
38495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3850ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
385182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
385289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
385389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
3854ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3855ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3856ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
385748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
385882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
38594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
386048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
386148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
38624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
386348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
386448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
386548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
38665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
3867763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
38684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
38694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
38704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
38715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
38725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
3873763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
38744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
38754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
38764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
38774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
38784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
38795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
38805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
388182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
38824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
38834da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
3884e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3885e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3886e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
3887e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
388882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3889e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3890e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3891e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
38924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
389382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
38944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
38954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
3896a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
3897a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
3898a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
3899a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
39004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3901a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
3902a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
3903a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
3904e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
3905a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
3906a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3907e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
3908b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, number);
3909e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
3910e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov
3911e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
3912e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
3913e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
3914a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
3915a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
3916a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3917ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
3918b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3919ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
392089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3921ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3922ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3923ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3924ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3925b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3926ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
39274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
39284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
3929ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3930ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3931ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3932ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
3933b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
3934ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
393589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
393689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
3937ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3938ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3939ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3940b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
39410c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
3942b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
3943b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3944b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3945b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
394631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
3947d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
39482d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
39492d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
39502d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
39512d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
395231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
3953d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
3954d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
395531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
395631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
395731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
395831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3959763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
39607581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
39617581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
39622d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        maxSuggestions, filter);
396331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
396431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3965eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3966eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
3967eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
396889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3969e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3970e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
3971e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
3972b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
3973e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
397482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3975b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
3976e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3977e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
397882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3979b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
3980e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3981e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3982e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3983eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3984eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3985eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
398682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
39870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
39885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
39895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
39905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
399182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
39920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
39934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
39944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
39955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
39965ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
39975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
3998c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
3999a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
4000c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4001c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4002c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
4003b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4004b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, contactId, projection);
4005c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4006c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
40071b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
4008b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
40091b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
40101b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
40111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
40121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
4013b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
40141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
40151b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
40161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
40171b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
40181b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
4019b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
40201b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
40211b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
40221b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
40231b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
40241b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
4025b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
40261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
402771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
40281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
40291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
40301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
403146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
403246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
403346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
403446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
403546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
403646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
403746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
403846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
40394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
40404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
404146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
404246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
404346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
40444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
4045f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
4046c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
40474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
40484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
40505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
40535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
40545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
4055038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
4056038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
4057038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
4058038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
40595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
40605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
40614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
40624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
40634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
40644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
40654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
40664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
40685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
40695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
40705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = lookupContactIdBySourceIds(db, segments);
40725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (contactId == -1) {
40735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
40745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
40775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
40805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
40815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
40835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
40845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
40855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
40865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
40875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
40885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
40905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
40915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
40925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
40935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
40965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
40975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int sourceIdCount = 0;
40985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
40995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
41005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
41015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sourceIdCount++;
41025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (sourceIdCount == 0) {
41065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
41075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
41105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
41115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
41125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
41135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
41145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
41155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
41165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
41175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
41205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
41215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
41235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
41245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
41255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
41265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
41275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
41285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
41295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
41305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
41315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
41325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
41335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (segment.sourceIdLookup && accountHashCode == segment.accountHashCode
41345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
41355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
41365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
41375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
41385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
41395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
41415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
41425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
41455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupByDisplayNameQuery {
41485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
41495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
41515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
41525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
41535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
41545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
41555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
41565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
41585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
41595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
41605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int NORMALIZED_NAME = 3;
41615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
41645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
41655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int displayNameCount = 0;
41665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
41675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
41685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
41695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                displayNameCount++;
41705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (displayNameCount == 0) {
41745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
41755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
41785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
41795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
41805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
41815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
41825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
41835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
41845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
41855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
41885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
41895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
41905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
41925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
41935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
41945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
41955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
41965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
41975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
41985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
41995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
42005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
42015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
42025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (!segment.sourceIdLookup && accountHashCode == segment.accountHashCode
42035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
42045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
42055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
42065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
42075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
42085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
42095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
42105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
42115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
42145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
42155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
42175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
42185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
42195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
42205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
42215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
42235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
42245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
42265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
42275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
42295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
42305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
42315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
42325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
42335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
42345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
42355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
42365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
42375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
42385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
42395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
42405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
42415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
42425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
42435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
42455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
42465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
42475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
42485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
42505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4251763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
4252763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
425382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4254763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4255f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4256763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4257763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4258d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4259763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4260763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
4261b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
426282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_PRESENCE)) {
426382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
426482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ")");
426582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
4266b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
426782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS,
426882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
426982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
427082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
427182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
42723296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
42733296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
4274a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
42753296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
427682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
427782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
427882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
427982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
4280ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
4281763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
4282763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
4283763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4284f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4285763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4286763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4287d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4288763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4289763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
4290763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
4291763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
4292763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
4293763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
4294763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
429546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private void setTablesAndProjectionMapForRawContactsEntities(SQLiteQueryBuilder qb, Uri uri) {
429646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        // Note: currently, "export only" equals to "restricted", but may not in the future.
429746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
429846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                Data.FOR_EXPORT_ONLY, false);
429946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
4300f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
430146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
430246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        if (requestingPackage != null) {
430346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            excludeRestrictedData = excludeRestrictedData
430446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
430546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        }
430646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setTables(mDbHelper.getContactEntitiesView(excludeRestrictedData));
430746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setProjectionMap(sRawContactsEntityProjectionMap);
430846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
430946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
431046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
431182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
431282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
431382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4314d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa        // Note: currently, "export only" equals to "restricted", but may not in the future.
4315763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
4316d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa                Data.FOR_EXPORT_ONLY, false);
4317763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4318f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4319763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4320763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4321763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            excludeRestrictedData = excludeRestrictedData
4322d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4323763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4324763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4325763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getDataView(excludeRestrictedData));
432682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
432782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
43283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated presence when requested
4329b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.CONTACT_PRESENCE)) {
433082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
43313296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + "="
433282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    + RawContacts.CONTACT_ID + ")");
433382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
433482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
43353296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated status updates when requested
4336b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
433782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS,
433882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_RES_PACKAGE,
433982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_ICON,
434082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_LABEL,
434182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_TIMESTAMP)) {
43423296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
43433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
434482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
43453296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
4346ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        }
43473296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
43483296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual presence when requested
43493296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection, Data.PRESENCE)) {
43503296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
43513296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdates.DATA_ID + "="
43523296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + DataColumns.CONCRETE_ID + ")");
43533296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
43543296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
43553296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual status updates when requested
43563296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection,
43573296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS,
43583296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_RES_PACKAGE,
43593296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_ICON,
43603296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_LABEL,
43613296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_TIMESTAMP)) {
43623296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
43633296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
43643296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + DataColumns.CONCRETE_ID + ")");
43653296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
43663296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
436782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
436882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);
436982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
4370ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
4371ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
43720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
43730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
43740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4375b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
43760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
43770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4378b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, StatusUpdates.PRESENCE)) {
43790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
43800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID
43810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
43820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
43830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4384b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
43850a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
43860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
43870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
43880a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
43890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
43900a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
43910a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.STATUS_UPDATES + "." + StatusUpdatesColumns.DATA_ID
43920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
43930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
43940a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setTables(sb.toString());
43950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
43960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
43970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
43984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
4399f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
4400f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
44014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (!TextUtils.isEmpty(accountName)) {
44024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
44034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
44044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
44054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
44064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
44074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
44084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
44094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
44104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4411e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
4412f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
4413f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
4414e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        if (!TextUtils.isEmpty(accountName)) {
4415e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
4416e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4417e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
4418e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
4419e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
4420e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
4421e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
4422e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
4423e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
4424e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
4425e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
4426e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
4427e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
4428e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
4429e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
44307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
4431c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
4432c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
4433c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
4434c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
4435c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
4436f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
4437f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String limitParam = getQueryParameter(uri, "limit");
4438c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
4439c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4440c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4441c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
4442c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
4443c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
4444c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
4445c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
4446c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
4447c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4448c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
4449c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
4450c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
4451c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4452c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4453c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4454c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
44555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /**
44565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
44575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
44585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     */
44595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
44605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        int len = cons.length();
44615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
44625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        for (int i = 0; i < len; i++) {
44635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            char c = cons.charAt(i);
44645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
44655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
44665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
44675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
44685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
44695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    || (c == '#') || (c == '*')) {
44705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
44715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
44725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
44735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
44745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
44755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
44765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
44775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
44785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
44795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            return false;
44805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        }
44815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
44825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        return true;
44835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
44845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
448500ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
4486d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
448770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
448870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
4489fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
449070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
449170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
449270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
449370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
4494d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
449570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
449667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
44975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
44985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
4499619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
4500619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
4501619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4502b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
4503b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
4504b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
4505b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
4506d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_PHOTO: {
4507b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                if (!"r".equals(mode)) {
4508b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                    throw new FileNotFoundException("Mode " + mode + " not supported.");
4509b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                }
4510b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4511b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                String sql =
4512b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
4513b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                        " WHERE " + Data._ID + "=" + Contacts.PHOTO_ID
45144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                                + " AND " + RawContacts.CONTACT_ID + "=?";
4515b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
45164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql,
45174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
4518d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4519d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4520f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4521d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
4522d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
452314b8a1243ab5c043b35e47527ca1c962064f3771Daisuke Miyakawa                final String selection = Contacts._ID + "=" + contactId;
4524d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4525d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
4526d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
4527d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
4528d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
4529d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                outputRawContactsAsVCard(localStream, selection, null);
4530d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return buildAssetFileDescriptor(localStream);
4531d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4532b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4533b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
4534b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                throw new FileNotFoundException("No file at: " + uri);
4535b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
4536b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
4537b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4538d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
4539d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String VCARD_TYPE_DEFAULT = "default";
4540d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4541d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4542d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
4543d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
4544d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4545d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
4546d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        AssetFileDescriptor fd = null;
4547d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
4548d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
4549d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4550d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
4551d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final int size = byteData.length;
4552d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4553d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
4554d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.writeBytes(byteData, 0, 0, size);
4555d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.deactivate();
4556b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4557d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
4558d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
4559d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
4560d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4561d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        return fd;
4562d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4563d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4564d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4565d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
4566d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
4567d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
4568d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4569d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
4570d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
4571d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
4572d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final VCardComposer composer = new VCardComposer(context, VCARD_TYPE_DEFAULT, false);
4573d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
4574d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4575f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
4576d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        if (!composer.init(selection, selectionArgs))
4577d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
4578d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4579d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
4580d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
4581d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
4582d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4583d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4584d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
4585d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4586b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
45874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
45884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
4589a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
45904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
4591b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
4592b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP:
4593be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
4594b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
4595b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
4596b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
4597f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
4598f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
4599b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
4600be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
4601b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
4602b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
4603508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
4604b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
460548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
460648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
460748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
460848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
460948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
461048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
461148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
461248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
461348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
461448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
461548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
461648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
4617b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
4618b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
4619b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
4620b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
4621b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
4622b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
4623b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
4624b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
4625c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
4626c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
4627c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
4628c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
462961efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
463061efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
46314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
46324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
46337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
463425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private void setDisplayName(long rawContactId, String displayName, int bestDisplayNameSource) {
46353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (displayName != null) {
463625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindString(1, displayName);
46373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } else {
463825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindNull(1);
46393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
464025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(2, bestDisplayNameSource);
464125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(3, rawContactId);
464225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
46433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
46443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
464573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
464673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
464773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
464873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
4649a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.add(rawContactId);
465073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
465173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4652c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4653c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
4654c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
4655c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4656c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4657c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4658653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
4659c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
4660653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
4661653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
4662c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
4663c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4664c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
4665c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4666c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
4667c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
4668c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4669c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4670c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4671653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
4672c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
4673653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
4674653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
4675c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
4676c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4677ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4678f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForEmail(long rawContactId, long dataId, String email) {
4679f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
4680f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4681f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4682f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4683f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
4684f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (tokens.length == 0) {
4685f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4686f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4687f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4688f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String address = tokens[0].getAddress();
4689f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int at = address.indexOf('@');
4690f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (at != -1) {
4691f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            address = address.substring(0, at);
4692f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4693f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4694f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4695f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
4696f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4697f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4698f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4699f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
4700f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4701f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
4702f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
4703f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4704f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4705f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4706f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4707f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
4708f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4709f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4710a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
4711a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
4712a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
4713a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
4714a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
4715a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
4716a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
4717a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
4718a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
4719a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
4720a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
4721f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4722f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name) {
4723f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name);
4724f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4725f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4726315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private interface NicknameLookupPreloadQuery {
4727315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
4728315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
4729315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String[] COLUMNS = new String[] {
4730315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            NicknameLookupColumns.NAME
4731315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        };
4732315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
4733315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        int NAME = 0;
4734315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    }
4735315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
4736315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    /**
4737315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * Read all known common nicknames from the database and populate a Bloom
4738315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * filter using the corresponding hash codes. The idea is to eliminate most
4739315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * of unnecessary database lookups for nicknames. Given a name, we will take
47403684089aba82df3f7a0c111e7c96ed8b0380e57aDmitri Plotnikov     * its hash code and see if it is set in the Bloom filter. If not, we will know
4741315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * that the name is not in the database. If it is, we still need to run a
4742315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * query.
4743315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * <p>
4744315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * Given the size of the filter and the expected size of the nickname table,
4745315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * we should expect the combination of the Bloom filter and cache will
4746315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * prevent around 98-99% of unnecessary queries from running.
4747315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     */
4748315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private void preloadNicknameBloomFilter() {
4749315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        mNicknameBloomFilter = new BitSet(NICKNAME_BLOOM_FILTER_SIZE + 1);
4750315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
4751315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupPreloadQuery.TABLE,
4752315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                NicknameLookupPreloadQuery.COLUMNS,
4753315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                null, null, null, null, null);
4754315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        try {
4755315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            int count = cursor.getCount();
4756315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            for (int i = 0; i < count; i++) {
4757315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                cursor.moveToNext();
4758315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                String normalizedName = cursor.getString(NicknameLookupPreloadQuery.NAME);
4759315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                int hashCode = normalizedName.hashCode();
4760315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                mNicknameBloomFilter.set(hashCode & NICKNAME_BLOOM_FILTER_SIZE);
4761315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            }
4762315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        } finally {
4763315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            cursor.close();
4764315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        }
4765315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    }
4766315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
4767315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
4768f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4769f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Returns nickname cluster IDs or null. Maintains cache.
4770f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4771f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] getCommonNicknameClusters(String normalizedName) {
4772315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        int hashCode = normalizedName.hashCode();
4773315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        if (!mNicknameBloomFilter.get(hashCode & NICKNAME_BLOOM_FILTER_SIZE)) {
4774315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            return null;
4775315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        }
4776315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
4777f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        SoftReference<String[]> ref;
4778f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
4779f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        synchronized (mNicknameClusterCache) {
4780f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (mNicknameClusterCache.containsKey(normalizedName)) {
4781f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                ref = mNicknameClusterCache.get(normalizedName);
4782f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                if (ref == null) {
4783f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    return null;
4784f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
4785f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = ref.get();
4786f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4787f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4788f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4789f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (clusters == null) {
4790f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            clusters = loadNicknameClusters(normalizedName);
4791f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ref = clusters == null ? null : new SoftReference<String[]>(clusters);
4792f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            synchronized (mNicknameClusterCache) {
4793f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                mNicknameClusterCache.put(normalizedName, ref);
4794f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4795f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4796f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
4797f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4798f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4799315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private interface NicknameLookupQuery {
4800315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
4801315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
4802315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String[] COLUMNS = new String[] {
4803315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            NicknameLookupColumns.CLUSTER
4804315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        };
4805315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
4806315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        int CLUSTER = 0;
4807315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    }
4808315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
4809f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] loadNicknameClusters(String normalizedName) {
4810b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
4811f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
4812f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupQuery.TABLE, NicknameLookupQuery.COLUMNS,
4813f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
4814f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                null, null, null);
4815f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        try {
4816f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            int count = cursor.getCount();
4817f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (count > 0) {
4818f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = new String[count];
4819f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                for (int i = 0; i < count; i++) {
4820f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    cursor.moveToNext();
4821f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    clusters[i] = cursor.getString(NicknameLookupQuery.CLUSTER);
4822f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
4823f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4824f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        } finally {
4825f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            cursor.close();
4826f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4827f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
4828f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4829f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4830f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
4831f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4832f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
4833f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
4834f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4835f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4836f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4837f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
4838f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
4839f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
4840f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4841f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4842f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4843f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
4844f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return ContactsProvider2.this.getCommonNicknameClusters(normalizedName);
4845f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4846f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4847f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4848f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4849f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
4850f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4851f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
4852f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, rawContactId);
4853f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, dataId);
4854f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, lookupType);
4855f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 4, name);
4856f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
4857f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4858f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4859f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4860f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
4861f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4862f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
4863f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupDelete, 1, dataId);
4864f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
4865f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4866f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
48672d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
4868d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
4869d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
4870d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
4871d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
4872d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
4873d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
4874d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
4875e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
4876d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN("
4877d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NAME_COLLATION_KEY + ","
4878d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.EMAIL_BASED_NICKNAME + ","
4879d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NICKNAME + ","
4880d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.ORGANIZATION + "))");
4881e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
4882e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
48835ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
4884c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
48857318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam);
4886c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
4887c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4888c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
48897318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam) {
48907318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), true);
48915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
48925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
48935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
48947318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov            boolean allowEmailMatch) {
4895d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
4896dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov                "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
4897d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
4898d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
4899d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " GLOB '");
49005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
4901a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
4902a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NAME_COLLATION_KEY + ","
4903a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NICKNAME + ","
490420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                + NameLookupType.ORGANIZATION);
490520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        if (allowEmailMatch) {
490620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
490720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        }
49087318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        sb.append("))");
4909ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
4910ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
49114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
49124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
49134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
49144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
4915b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
4916b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
4917b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
4918b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
4919b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
49204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
49214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
4922b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
4923b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
4924b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
4925caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
49265e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
49275e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
49285e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
49295e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
49305e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
49315e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
49325e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
49335e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
49345e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
49355e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
49365e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
4937caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
4938caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
4939caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
4940df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
4941df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
4942caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
4943caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
4944caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
4945caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
49466f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
4947caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
49486f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
4949caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
4950f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
4951627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    protected boolean isWritableAccount(Account account) {
4952627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
4953627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4954627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
4955627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
4956627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        account.type.equals(sync.accountType)) {
4957627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    return sync.supportsUploading();
4958627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4959627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4960627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
4961627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
4962627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
4963627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        return false;
4964627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4965b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
4966f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
4967f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
4968f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
4969f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
4970f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
4971f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
4972f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
4973f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
4974f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
4975f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
4976f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
4977f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
4978f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
4979f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
4980f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
4981f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
4982f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
4983f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
4984f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
4985f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
4986f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
4987f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
4988f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
4989f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
4990f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
4991f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
4992f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
4993f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
4994f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
4995f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
4996f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
4997f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
4998f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
4999f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
5000f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5001f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5002f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
5003f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
5004f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5005f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
5006f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
5007f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
5008f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
5009f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
5010f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5011f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5012f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5013f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
5014f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5015f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
5016f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5017f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5018f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5019f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
5020f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
5021f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
5022f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5023f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5024f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5025f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
5026f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
5027f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
5028f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
5029f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
5030f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5031f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5032f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
5033f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
50344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
5035