ContactsProvider2.java revision 4da1b8ded4435a3392bac3511c67182015e0953f
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
19d9ec58265ae59a549880ef63cdfb5d0d977cdabaDmitri Plotnikovimport com.android.internal.content.SyncStateContentProviderHelper;
2053214b3ed12b0ff9cb589b6559311f2ac142f2e3Bjorn Bringertimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
215b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
2297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
2497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
2597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
2697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
2797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DisplayNameSources;
2897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
2997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
3097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NicknameLookupColumns;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
3797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
3897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
3997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
4097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
4197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
4297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
4397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
4497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.accounts.Account;
4597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.accounts.AccountManager;
46b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.OnAccountsUpdateListener;
47caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.app.SearchManager;
485b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.content.ContentProviderOperation;
49bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.ContentProviderResult;
50bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.ContentResolver;
51bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.ContentUris;
52c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.content.ContentValues;
53568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.Context;
54568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.Entity;
556ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.EntityIterator;
5635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.IContentService;
5767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.OperationApplicationException;
5867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.SharedPreferences;
59627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
60bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.UriMatcher;
61568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.SharedPreferences.Editor;
623d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.res.AssetFileDescriptor;
63627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.database.CharArrayBuffer;
6467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.database.Cursor;
65f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.database.DatabaseUtils;
66e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteContentHelper;
68e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
69ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.sqlite.SQLiteDatabase;
70ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.sqlite.SQLiteQueryBuilder;
7109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.sqlite.SQLiteStatement;
7209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.net.Uri;
734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.os.Bundle;
7408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.os.MemoryFile;
754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.os.RemoteException;
764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.os.SystemProperties;
77d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.pim.vcard.VCardComposer;
78bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.preference.PreferenceManager;
796ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.provider.BaseColumns;
80bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.provider.ContactsContract;
81bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.provider.LiveFolders;
82bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.provider.OpenableColumns;
83ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.provider.SyncStateContract;
84bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
85b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.Contacts;
8615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.provider.ContactsContract.Data;
870dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.provider.ContactsContract.Groups;
880e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.provider.ContactsContract.PhoneLookup;
893d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
90508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.ContactsContract.Settings;
913de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
92b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
9397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
9497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
9597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
9697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
976d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
9897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
9997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
10097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
10197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
10297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.telephony.PhoneNumberUtils;
103ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.text.TextUtils;
1043de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.text.util.Rfc822Token;
1055b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.text.util.Rfc822Tokenizer;
1063de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.util.Log;
107d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1083de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport java.io.ByteArrayOutputStream;
109bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport java.io.FileNotFoundException;
1103de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport java.io.IOException;
11109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport java.io.OutputStream;
1123de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport java.lang.ref.SoftReference;
113916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport java.util.ArrayList;
1143de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport java.util.BitSet;
11582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport java.util.Collections;
11697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport java.util.HashMap;
11797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport java.util.HashSet;
11897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport java.util.List;
119a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport java.util.Locale;
1209a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport java.util.Map;
121a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport java.util.Set;
122c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
124108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa/**
125d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey * Contacts content provider. The contract between this provider and applications
126b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov * is defined in {@link ContactsContract}.
127d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey */
128d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeypublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
129108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa
130108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa    private static final String TAG = "ContactsProvider";
13142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
1327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
13442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
135b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    // TODO: check for restricted flag during insert(), update(), and delete() calls
1360e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
1375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
138622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
139b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1400e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    /**
141ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov     * Shared preference key for the legacy contact import version. The need for a version
1424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton     * we can trigger re-import by incrementing the import version.
1444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton     */
1454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final String PREF_CONTACTS_IMPORTED = "contacts_imported_v1";
1464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final int PREF_CONTACTS_IMPORT_VERSION = 1;
1475b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintana
148caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
149bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
150bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
151bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
1524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final String TIMES_CONTACED_SORT_COLUMN = "times_contacted_sort";
15315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
15415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
15515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            + TIMES_CONTACED_SORT_COLUMN + " DESC, "
15615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
15715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final String STREQUENT_LIMIT =
15815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
15905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
16005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
16105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int CONTACTS = 1000;
16205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
163619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private static final int CONTACTS_LOOKUP = 1002;
1643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
1653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int CONTACTS_DATA = 1004;
1663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1673d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
168b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
1703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int CONTACTS_PHOTO = 1009;
1713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int CONTACTS_AS_VCARD = 1010;
172b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov
173b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
17451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
1753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
1760dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int RAW_CONTACT_ENTITY_ID = 2005;
1770dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
1780dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int DATA = 3000;
1790e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final int DATA_ID = 3001;
1800e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final int PHONES = 3002;
181a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final int PHONES_ID = 3003;
1824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final int PHONES_FILTER = 3004;
183dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov    private static final int EMAILS = 3005;
1845e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final int EMAILS_ID = 3006;
185d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
186dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
1879b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi    private static final int POSTALS = 3009;
188d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final int POSTALS_ID = 3010;
189d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
190d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
191d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1926e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    private static final int AGGREGATION_EXCEPTIONS = 6000;
1939b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori    private static final int AGGREGATION_EXCEPTION_ID = 6001;
1949b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
1959b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori    private static final int STATUS_UPDATES = 7000;
1969b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori    private static final int STATUS_UPDATES_ID = 7001;
1976e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
1989b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori    private static final int AGGREGATION_SUGGESTIONS = 8000;
1999b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2009b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori    private static final int SETTINGS = 9000;
2019b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
202de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    private static final int GROUPS = 10000;
203de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    private static final int GROUPS_ID = 10001;
2043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    private static final int GROUPS_SUMMARY = 10003;
2053716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
2063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    private static final int SYNCSTATE = 11000;
2073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    private static final int SYNCSTATE_ID = 11001;
208d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
209d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
2105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
2115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
212a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
217a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int RAW_CONTACT_ENTITIES = 15001;
218f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
21942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private interface DataContactsQuery {
2202149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        public static final String TABLE = "data "
2212149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
222a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
223a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
224a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        public static final String[] PROJECTION = new String[] {
2254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            RawContactsColumns.CONCRETE_ID,
2265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            DataColumns.CONCRETE_ID,
2275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
2285ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        };
22946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
2304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        public static final int RAW_CONTACT_ID = 0;
2316bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        public static final int DATA_ID = 1;
2326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        public static final int CONTACT_ID = 2;
233ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
23448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
23548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private interface DataDeleteQuery {
23648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
23748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
23848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
23948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            DataColumns.CONCRETE_ID,
24048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
24148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            Data.RAW_CONTACT_ID,
242a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            Data.IS_PRIMARY,
2436bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            Data.DATA1,
2446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        };
245b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
246b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
247b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            Data._ID,
24882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
24982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            Data.RAW_CONTACT_ID,
2501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            Data.IS_PRIMARY,
25131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            Data.DATA1,
25231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        };
253eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
254eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        public static final int _ID = 0;
255ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int MIMETYPE = 1;
256ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int RAW_CONTACT_ID = 2;
257ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int IS_PRIMARY = 3;
258ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int DATA1 = 4;
25935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    }
260b5a4add17815167d20a90645779df34cdf45280dFred Quintana
26135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private interface DataUpdateQuery {
262c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
263c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
264c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        int _ID = 0;
2651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        int RAW_CONTACT_ID = 1;
2661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        int MIMETYPE = 2;
2671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    }
2681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
2691b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
27046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private interface RawContactsQuery {
27146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        String TABLE = Tables.RAW_CONTACTS;
27209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
27309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        String[] COLUMNS = new String[] {
274d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                RawContacts.DELETED,
275d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
276d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
2777a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        };
2787a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
279dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        int DELETED = 0;
280dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        int ACCOUNT_TYPE = 1;
281dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        int ACCOUNT_NAME = 2;
282dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
283dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
284dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
285dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
286dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
287dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    /** Sql where statement for filtering on groups. */
288dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String CONTACTS_IN_GROUP_SELECT =
289dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Contacts._ID + " IN "
290dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "(SELECT " + RawContacts.CONTACT_ID
291dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS
292dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
293dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
294dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
295dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
296dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
297dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
298dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " FROM " + Tables.GROUPS
299dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " WHERE " + Groups.TITLE + "=?)))";
300dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
301dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    /** Sql for updating DIRTY flag on multiple raw contacts */
302dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
303dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "UPDATE " + Tables.RAW_CONTACTS +
304dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            " SET " + RawContacts.DIRTY + "=1" +
305dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            " WHERE " + RawContacts._ID + " IN (";
306e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
307e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
308e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
309e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
310e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
311e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
312e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
313e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains just BaseColumns._COUNT */
314e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sCountProjectionMap;
315e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains just the contacts columns */
316e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sContactsProjectionMap;
317e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Used for pushing starred contacts to the top of a times contacted list **/
318e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sStrequentStarredProjectionMap;
319e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sStrequentFrequentProjectionMap;
320e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains just the contacts vCard columns */
321e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sContactsVCardProjectionMap;
322e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains just the raw contacts columns */
323e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sRawContactsProjectionMap;
324e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains the columns from the raw contacts entity view*/
325e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sRawContactsEntityProjectionMap;
326e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains columns from the data view */
327e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sDataProjectionMap;
328e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains columns from the data view */
329e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sDistinctDataProjectionMap;
330e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains the data and contacts columns, for joined tables */
331e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sPhoneLookupProjectionMap;
332e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains the just the {@link Groups} columns */
333e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sGroupsProjectionMap;
334e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains {@link Groups} columns along with summary details */
335e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
336e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains the agg_exceptions columns */
337e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
338e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    /** Contains the agg_exceptions columns */
339d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sSettingsProjectionMap;
340f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    /** Contains StatusUpdates columns */
341f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private static final HashMap<String, String> sStatusUpdatesProjectionMap;
342f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    /** Contains Live Folders columns */
34367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private static final HashMap<String, String> sLiveFoldersProjectionMap;
34467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
3456cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov    // where clause to update the status_updates table
3463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
347f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
348ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
349ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
350d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
35167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    /** Precompiled sql statement for setting a data record to the primary. */
352d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private SQLiteStatement mSetPrimaryStatement;
353ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Precompiled sql statement for setting a data record to the super primary. */
3541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private SQLiteStatement mSetSuperPrimaryStatement;
355f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    /** Precompiled sql statement for incrementing times contacted for a contact */
35619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private SQLiteStatement mContactsLastTimeContactedUpdate;
35719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    /** Precompiled sql statement for updating a contact display name */
35819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private SQLiteStatement mRawContactDisplayNameUpdate;
359ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    /** Precompiled sql statement for updating an aggregated status update */
360ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private SQLiteStatement mLastStatusUpdate;
361ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
36219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private SQLiteStatement mNameLookupDelete;
36319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private SQLiteStatement mStatusUpdateAutoTimestamp;
36419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private SQLiteStatement mStatusUpdateInsert;
365ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private SQLiteStatement mStatusUpdateReplace;
366ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private SQLiteStatement mStatusAttributionUpdate;
36719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private SQLiteStatement mStatusUpdateDelete;
36819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
369c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    private long mMimeTypeIdEmail;
370caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    private long mMimeTypeIdIm;
37171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private long mMimeTypeIdStructuredName;
37271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private long mMimeTypeIdOrganization;
37371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private long mMimeTypeIdNickname;
37471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private long mMimeTypeIdPhone;
37571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
37671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
37771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
37871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private String[] mSelectionArgs3 = new String[3];
37971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private Account mAccount;
38071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
38171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    static {
38271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov        // Contacts URI matching table
38371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov        final UriMatcher matcher = sUriMatcher;
38471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
385a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
386a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
387a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
388a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
389a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
390a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
391a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO);
392a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
393a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
394a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
395a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
396a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
397c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
398c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
399c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
400c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
401c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
402c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
403c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
404c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
405c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
406c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
407c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
408c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
409c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
410c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
411c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
412c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
413c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
414c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
415c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
416c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
417c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
418c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
419c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
420c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
421c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
422c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
423916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
424916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
425916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
426916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
42792ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
428916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                SYNCSTATE_ID);
429f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
430f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
431f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
432f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
433f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
434f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
435f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
436f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
437f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
438f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
439f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
440f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
441f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
442f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                SEARCH_SUGGESTIONS);
443f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
444916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                SEARCH_SUGGESTIONS);
445f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/#",
446f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                SEARCH_SHORTCUT);
447f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
448f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
449f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
450f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
451f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
452f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
453f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
454f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
455f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
4563d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov    }
4573d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov
458f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    static {
459f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sCountProjectionMap = new HashMap<String, String>();
460f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
461f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
462f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap = new HashMap<String, String>();
463cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov        sContactsProjectionMap.put(Contacts._ID, Contacts._ID);
464f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
465f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
466f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
467f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
468f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
469f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
470f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
471f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap.put(Contacts.HAS_PHONE_NUMBER, Contacts.HAS_PHONE_NUMBER);
472f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
473f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
474f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
475f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // Handle projections for Contacts-level statuses
476f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sContactsProjectionMap, Contacts.CONTACT_PRESENCE,
477f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
478f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS,
479f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
480f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
481f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
482f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
483f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
48403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_LABEL,
485f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
486f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_ICON,
487f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
488f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
489f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sStrequentStarredProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
490f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sStrequentStarredProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
491f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                  Long.MAX_VALUE + " AS " + TIMES_CONTACED_SORT_COLUMN);
492f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
493f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sStrequentFrequentProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
494f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sStrequentFrequentProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
495f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                  Contacts.TIMES_CONTACTED + " AS " + TIMES_CONTACED_SORT_COLUMN);
496f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
497f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsVCardProjectionMap = Maps.newHashMap();
498f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsVCardProjectionMap.put(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME
499f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + " || '.vcf' AS " + OpenableColumns.DISPLAY_NAME);
500f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sContactsVCardProjectionMap.put(OpenableColumns.SIZE, "0 AS " + OpenableColumns.SIZE);
501f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
502f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap = new HashMap<String, String>();
503f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
504f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
505f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
506f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
507f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
508f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
509f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
510f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DELETED, RawContacts.DELETED);
511f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.TIMES_CONTACTED, RawContacts.TIMES_CONTACTED);
512f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.LAST_TIME_CONTACTED,
513f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                RawContacts.LAST_TIME_CONTACTED);
514f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CUSTOM_RINGTONE, RawContacts.CUSTOM_RINGTONE);
515f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SEND_TO_VOICEMAIL, RawContacts.SEND_TO_VOICEMAIL);
516f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.STARRED, RawContacts.STARRED);
517f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
518f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC1, RawContacts.SYNC1);
519f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC2, RawContacts.SYNC2);
520f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC3, RawContacts.SYNC3);
521f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC4, RawContacts.SYNC4);
522f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
523f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap = new HashMap<String, String>();
524f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data._ID, Data._ID);
525f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA1, Data.DATA1);
532f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA2, Data.DATA2);
533f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA3, Data.DATA3);
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA4, Data.DATA4);
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA5, Data.DATA5);
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA6, Data.DATA6);
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA7, Data.DATA7);
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA8, Data.DATA8);
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA9, Data.DATA9);
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA10, Data.DATA10);
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA11, Data.DATA11);
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA12, Data.DATA12);
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA13, Data.DATA13);
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA14, Data.DATA14);
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.DATA15, Data.DATA15);
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Data.CONTACT_ID, Data.CONTACT_ID);
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
558038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
563e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        HashMap<String, String> columns;
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns = new HashMap<String, String>();
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts._ID, RawContacts._ID);
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
572916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        columns.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts.VERSION, RawContacts.VERSION);
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts.DIRTY, RawContacts.DIRTY);
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts.DELETED, RawContacts.DELETED);
577916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        columns.put(RawContacts.IS_RESTRICTED, RawContacts.IS_RESTRICTED);
5785e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        columns.put(RawContacts.SYNC1, RawContacts.SYNC1);
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts.SYNC2, RawContacts.SYNC2);
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts.SYNC3, RawContacts.SYNC3);
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts.SYNC4, RawContacts.SYNC4);
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.MIMETYPE, Data.MIMETYPE);
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA1, Data.DATA1);
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA2, Data.DATA2);
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA3, Data.DATA3);
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA4, Data.DATA4);
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA5, Data.DATA5);
589f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        columns.put(Data.DATA6, Data.DATA6);
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA7, Data.DATA7);
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA8, Data.DATA8);
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA9, Data.DATA9);
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA10, Data.DATA10);
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA11, Data.DATA11);
595ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        columns.put(Data.DATA12, Data.DATA12);
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA13, Data.DATA13);
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA14, Data.DATA14);
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA15, Data.DATA15);
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.SYNC1, Data.SYNC1);
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.SYNC2, Data.SYNC2);
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.SYNC3, Data.SYNC3);
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.SYNC4, Data.SYNC4);
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(RawContacts.Entity.DATA_ID, RawContacts.Entity.DATA_ID);
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.STARRED, Data.STARRED);
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sRawContactsEntityProjectionMap = columns;
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // Handle projections for Contacts-level statuses
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Contacts.CONTACT_PRESENCE,
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS,
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
617a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // Handle projections for Data-level statuses
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Data.PRESENCE,
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Data.STATUS,
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                StatusUpdatesColumns.CONCRETE_STATUS);
630a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        addProjection(sDataProjectionMap, Data.STATUS_TIMESTAMP,
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Data.STATUS_RES_PACKAGE,
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Data.STATUS_LABEL,
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDataProjectionMap, Data.STATUS_ICON,
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // Projection map for data grouped by contact (not raw contact) and some data field(s)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap = new HashMap<String, String>();
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data._ID,
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                "MIN(" + Data._ID + ") AS " + Data._ID);
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
6474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA1, Data.DATA1);
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA2, Data.DATA2);
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA3, Data.DATA3);
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA4, Data.DATA4);
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA5, Data.DATA5);
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA6, Data.DATA6);
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA7, Data.DATA7);
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA8, Data.DATA8);
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA9, Data.DATA9);
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA10, Data.DATA10);
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA11, Data.DATA11);
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA12, Data.DATA12);
6605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA13, Data.DATA13);
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA14, Data.DATA14);
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA15, Data.DATA15);
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
6709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDistinctDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sDistinctDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID,
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID);
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6803d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov        // Handle projections for Contacts-level statuses
6813d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_PRESENCE,
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS,
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
6882530512f639c4979fd7371c7dd25dd67e8118124Bai Tao                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
691ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // Handle projections for Data-level statuses
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Data.PRESENCE,
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Data.STATUS,
698f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                StatusUpdatesColumns.CONCRETE_STATUS);
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Data.STATUS_TIMESTAMP,
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
701f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Data.STATUS_RES_PACKAGE,
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Data.STATUS_LABEL,
704f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
705f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        addProjection(sDistinctDataProjectionMap, Data.STATUS_ICON,
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap = new HashMap<String, String>();
709c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
710f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsColumns.CONCRETE_ID + " AS " + PhoneLookup._ID);
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LOOKUP_KEY,
712f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Contacts.LOOKUP_KEY + " AS " + PhoneLookup.LOOKUP_KEY);
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsColumns.CONCRETE_DISPLAY_NAME + " AS " + PhoneLookup.DISPLAY_NAME);
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
716ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                ContactsColumns.CONCRETE_LAST_TIME_CONTACTED
717f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsColumns.CONCRETE_TIMES_CONTACTED + " AS " + PhoneLookup.TIMES_CONTACTED);
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsColumns.CONCRETE_STARRED + " AS " + PhoneLookup.STARRED);
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Contacts.IN_VISIBLE_GROUP + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Contacts.PHOTO_ID + " AS " + PhoneLookup.PHOTO_ID);
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsColumns.CONCRETE_CUSTOM_RINGTONE + " AS " + PhoneLookup.CUSTOM_RINGTONE);
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Contacts.HAS_PHONE_NUMBER + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
733373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Phone.TYPE + " AS " + PhoneLookup.TYPE);
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                Phone.LABEL + " AS " + PhoneLookup.LABEL);
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // Groups projection map
741eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns = new HashMap<String, String>();
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups._ID, Groups._ID);
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.DIRTY, Groups.DIRTY);
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.VERSION, Groups.VERSION);
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.RES_PACKAGE, Groups.RES_PACKAGE);
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.TITLE, Groups.TITLE);
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.DELETED, Groups.DELETED);
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.SHOULD_SYNC, Groups.SHOULD_SYNC);
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.SYNC1, Groups.SYNC1);
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.SYNC2, Groups.SYNC2);
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.SYNC3, Groups.SYNC3);
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.SYNC4, Groups.SYNC4);
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sGroupsProjectionMap = columns;
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // RawContacts and groups projection map
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns = new HashMap<String, String>();
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.putAll(sGroupsProjectionMap);
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + ") AS " + Groups.SUMMARY_COUNT);
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sGroupsSummaryProjectionMap = columns;
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // Aggregate exception projection map
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns = new HashMap<String, String>();
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
77982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID1);
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID2, AggregationExceptions.RAW_CONTACT_ID2);
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // Settings projection map
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns = new HashMap<String, String>();
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Settings.ANY_UNSYNCED, "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + ",(SELECT (CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL THEN 1 ELSE MIN("
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + Groups.SHOULD_SYNC + ") END) FROM " + Tables.GROUPS + " WHERE "
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0 THEN 1 ELSE 0 END) AS "
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + Settings.ANY_UNSYNCED);
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " GROUP BY "
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID + " HAVING " + Clauses.HAVING_NO_GROUPS
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + ")) AS " + Settings.UNGROUPED_COUNT);
8011b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + Contacts.HAS_PHONE_NUMBER + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + " HAVING " + Clauses.HAVING_NO_GROUPS + ")) AS "
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + Settings.UNGROUPED_WITH_PHONES);
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        sSettingsProjectionMap = columns;
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns = new HashMap<String, String>();
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(PresenceColumns.RAW_CONTACT_ID, PresenceColumns.RAW_CONTACT_ID);
810d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        columns.put(StatusUpdates.DATA_ID,
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                DataColumns.CONCRETE_ID + " AS " + StatusUpdates.DATA_ID);
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(StatusUpdates.IM_ACCOUNT, StatusUpdates.IM_ACCOUNT);
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(StatusUpdates.IM_HANDLE, StatusUpdates.IM_HANDLE);
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(StatusUpdates.PROTOCOL, StatusUpdates.PROTOCOL);
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // We cannot allow a null in the custom protocol field, because SQLite3 does not
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        // properly enforce uniqueness of null values
817f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(StatusUpdates.CUSTOM_PROTOCOL, "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + "='' THEN NULL ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END) AS "
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                + StatusUpdates.CUSTOM_PROTOCOL);
820778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov        columns.put(StatusUpdates.PRESENCE, StatusUpdates.PRESENCE);
821778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov        columns.put(StatusUpdates.STATUS, StatusUpdates.STATUS);
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov        columns.put(StatusUpdates.STATUS_TIMESTAMP, StatusUpdates.STATUS_TIMESTAMP);
8237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(StatusUpdates.STATUS_RES_PACKAGE, StatusUpdates.STATUS_RES_PACKAGE);
8249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        columns.put(StatusUpdates.STATUS_ICON, StatusUpdates.STATUS_ICON);
8259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        columns.put(StatusUpdates.STATUS_LABEL, StatusUpdates.STATUS_LABEL);
8269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        sStatusUpdatesProjectionMap = columns;
8279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
8289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // Live folder projection
8299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        sLiveFoldersProjectionMap = new HashMap<String, String>();
8302526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders._ID,
8312526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                Contacts._ID + " AS " + LiveFolders._ID);
832bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders.NAME,
833bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Contacts.DISPLAY_NAME + " AS " + LiveFolders.NAME);
834bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
835bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // TODO: Put contact photo back when we have a way to display a default icon
83651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        // for contacts without a photo
83703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP,
83803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        //      Photos.DATA + " AS " + LiveFolders.ICON_BITMAP);
83903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
84003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
84103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static void addProjection(HashMap<String, String> map, String toField, String fromField) {
8429a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        map.put(toField, fromField + " AS " + toField);
8439a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
8449a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
845f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    /**
8461129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov     * Handles inserts and update for a specific Data type.
8471129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov     */
8482526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private abstract class DataRowHandler {
8492526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
850f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        protected final String mMimetype;
851f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        protected long mMimetypeId;
8524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
8534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        @SuppressWarnings("all")
854a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        public DataRowHandler(String mimetype) {
855d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            mMimetype = mimetype;
856d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
857a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            // To ensure the data column position. This is dead code if properly configured.
858a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
8593653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
8603653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                    || Email.DATA != Data.DATA1) {
8612d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
8622d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        + " data is not in DATA1 column");
863a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
864c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        }
8655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
8665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        protected long getMimeTypeId() {
8672149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            if (mMimetypeId == 0) {
8685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
8692149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
8702149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            return mMimetypeId;
871a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
872a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
873a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        /**
874a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov         * Inserts a row into the {@link Data} table.
875f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey         */
87642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
87742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            final long dataId = db.insert(Tables.DATA, null, values);
8785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
879ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
880ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            if (primary != null && primary != 0) {
8815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
8823653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
8835ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
8845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return dataId;
8855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        }
88646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
88746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        /**
88846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana         * Validates data and updates a {@link Data} row using the cursor, which contains
889b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov         * the current data.
8904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton         */
8914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
892ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                boolean callerIsSyncAdapter) {
89348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
8945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
895ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
8964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
89748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
8981dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
8995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
9005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
9014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
902ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                values.remove(Data.IS_SUPER_PRIMARY);
90348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
9041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else if (values.containsKey(Data.IS_PRIMARY)) {
905ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                setIsPrimary(rawContactId, dataId, getMimeTypeId());
906ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
907ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Now that we've taken care of setting this, remove it from "values".
908ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.remove(Data.IS_PRIMARY);
90935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            }
910b5a4add17815167d20a90645779df34cdf45280dFred Quintana
911b5a4add17815167d20a90645779df34cdf45280dFred Quintana            if (values.size() > 0) {
91235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                mSelectionArgs1[0] = String.valueOf(dataId);
913a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                mDb.update(Tables.DATA, values, Data._ID + " =?", mSelectionArgs1);
914b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
915b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
916b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            if (!callerIsSyncAdapter) {
917b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                setRawContactDirty(rawContactId);
9184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
919eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        }
920eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
92182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
92282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
9231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
924c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
925c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
926c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=?", mSelectionArgs1);
927c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
9282d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
929c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (count != 0 && primary) {
930c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                fixPrimary(db, rawContactId);
9311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            }
9321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            return count;
9331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        }
9341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
9351b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
9361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            long mimeTypeId = getMimeTypeId();
9371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            long primaryId = -1;
9381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            int primaryType = -1;
93909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
94009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            Cursor c = db.query(DataDeleteQuery.TABLE,
941d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    DataDeleteQuery.CONCRETE_COLUMNS,
942d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    Data.RAW_CONTACT_ID + "=?" +
943d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                        " AND " + DataColumns.MIMETYPE_ID + "=" + mimeTypeId,
9447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                    mSelectionArgs1, null, null, null);
9457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            try {
94619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                while (c.moveToNext()) {
94719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
948d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
949d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
950d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                        primaryId = dataId;
951d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                        primaryType = type;
952d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    }
953d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
954d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            } finally {
955d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                c.close();
956d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
9574458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (primaryId != -1) {
9584458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                setIsPrimary(rawContactId, primaryId, mimeTypeId);
959d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
9603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
961ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
962ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        /**
963ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
964e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov         * row. Lower number represents higher priority.
965ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov         */
966ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        protected int getTypeRank(int type) {
967ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            return 0;
968ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
969ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
970a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
971e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
972e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                updateRawContactDisplayName(db, rawContactId);
973e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                mContactAggregator.updateDisplayName(db, rawContactId);
974e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            }
975e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        }
9763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
977b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        public boolean isAggregationRequired() {
97831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            return true;
9794097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        }
980f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
981315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        /**
982622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
983622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * as baseline, but augmented with any updates.
98472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov         */
985622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
986f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                ContentValues update) {
987a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov            final ContentValues values = new ContentValues();
988d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
989f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=?",
990a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov                    mSelectionArgs1, null, null, null);
99120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            try {
99273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                if (cursor.moveToFirst()) {
99320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    for (int i = 0; i < cursor.getColumnCount(); i++) {
99409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                        final String key = cursor.getColumnName(i);
9953826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                        values.put(key, cursor.getString(i));
99609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                    }
99715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
99815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } finally {
99915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                cursor.close();
1000bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
100173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            values.putAll(update);
1002d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return values;
1003de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
10041a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    }
10051a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
100681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
100781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
10084cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        public CustomDataRowHandler(String mimetype) {
10093826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            super(mimetype);
1010d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov        }
1011bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1012bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1013bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
10144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        private final NameSplitter mSplitter;
10154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1016de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        public StructuredNameRowHandler(NameSplitter splitter) {
1017ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1018ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            mSplitter = splitter;
1019ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1020ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov
1021ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        @Override
1022ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1023ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            fixStructuredNameComponents(values, values);
102435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1025ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
102615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
102715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
102815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name);
1029b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
103072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            return dataId;
1031a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        }
103265ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1033bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        @Override
103415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
103515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                boolean callerIsSyncAdapter) {
103672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            final long dataId = c.getLong(DataUpdateQuery._ID);
1037bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1038bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1039bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1040bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            fixStructuredNameComponents(augmented, values);
1041bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1042bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            super.update(db, values, c, callerIsSyncAdapter);
1043bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1044bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME)) {
1045bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                String name = values.getAsString(StructuredName.DISPLAY_NAME);
10462a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                deleteNameLookup(dataId);
104715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name);
1048bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1049bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1050bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1051bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
105205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        @Override
1053bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
105415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
10553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
105649d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov
10574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            int count = super.delete(db, c);
10584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1059767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov            deleteNameLookup(dataId);
106051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
106151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return count;
106204b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        }
106315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
106415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        /**
10654cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao         * Specific list of structured fields.
106604b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov         */
10674cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        private final String[] STRUCTURED_FIELDS = new String[] {
10684cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
106951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1070cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        };
10715b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
107215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        /**
10735b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov         * Parses the supplied display name, but only if the incoming values do
1074f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov         * not already contain structured name parts. Also, if the display name
10755b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov         * is not provided, generate one by concatenating first name and last
1076bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov         * name.
1077bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov         */
1078bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
10796d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1080bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
10816d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
1082bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
10836d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov
1084bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (touchedUnstruct && !touchedStruct) {
10856d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1086bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mSplitter.split(name, unstruct);
10876d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                name.toValues(update);
1088bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            } else if (!touchedUnstruct
10896d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
1090bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                // We need to update the display name when any structured components
1091bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
10926d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
1093bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
1094bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
10956d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                name.fromValues(augmented);
1096bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                final String joined = mSplitter.join(name);
1097bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                update.put(StructuredName.DISPLAY_NAME, joined);
10986d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            }
10996d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov        }
11006d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov    }
1101bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1102bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    public class StructuredPostalRowHandler extends DataRowHandler {
1103bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        private PostalSplitter mSplitter;
1104bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1105bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        public StructuredPostalRowHandler(PostalSplitter splitter) {
1106bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            super(StructuredPostal.CONTENT_ITEM_TYPE);
1107bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            mSplitter = splitter;
1108bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1109bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1110bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        @Override
1111bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1112bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            fixStructuredPostalComponents(values, values);
1113bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            return super.insert(db, rawContactId, values);
1114bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1115bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1116bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        @Override
1117bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1118bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean callerIsSyncAdapter) {
1119bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            final long dataId = c.getLong(DataUpdateQuery._ID);
112015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            final ContentValues augmented = getAugmentedValues(db, dataId, values);
112115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            fixStructuredPostalComponents(augmented, values);
112215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            super.update(db, values, c, callerIsSyncAdapter);
112315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
112415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
112515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        /**
112615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov         * Specific list of structured fields.
112715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov         */
1128bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        private final String[] STRUCTURED_FIELDS = new String[] {
112915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
113015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1131bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                StructuredPostal.COUNTRY,
1132bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
1133bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1134bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        /**
1135bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov         * Prepares the given {@link StructuredPostal} row, building
1136bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1137bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov         * values when missing. When structured components are missing, the
1138bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1139bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov         */
1140bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
1141bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
1142bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
114315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
114415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
114515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
114615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
114715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
114815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            if (touchedUnstruct && !touchedStruct) {
114915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mSplitter.split(postal, unstruct);
1150bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                postal.toValues(update);
1151bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            } else if (!touchedUnstruct
1152bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
1153bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                // See comment in
1154bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                postal.fromValues(augmented);
1155bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                final String joined = mSplitter.join(postal);
1156bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
1157bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1158bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1159bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1160bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1161fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
1162fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1163fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        private final String mTypeColumn;
1164fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        private final String mLabelColumn;
1165fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1166bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
1167bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            super(mimetype);
1168bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            mTypeColumn = typeColumn;
1169bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            mLabelColumn = labelColumn;
1170bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1171bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1172bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        @Override
117305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
117405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            enforceTypeAndLabel(values, values);
117505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            return super.insert(db, rawContactId, values);
117605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        }
117705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1178bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        @Override
1179bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1180bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean callerIsSyncAdapter) {
1181bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            final long dataId = c.getLong(DataUpdateQuery._ID);
1182bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1183bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            enforceTypeAndLabel(augmented, values);
1184bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            super.update(db, values, c, callerIsSyncAdapter);
1185bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1186bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1187bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        /**
1188bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1189bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov         * enforce that {@link #mLabelColumn} only appears when type is
11904cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
11914cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao         */
119253fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
11933826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
11943826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
11954f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
11964f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            if (hasLabel && !hasType) {
11974f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov                // When label exists, assert that some type is defined
1198fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
11994cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                        + mLabelColumn + " is defined.");
120051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
120151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
120251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
120351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
120451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
120551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
120651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        public OrganizationDataRowHandler() {
120751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
1208bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1209f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1210f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        @Override
1211f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1212f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            String company = values.getAsString(Organization.COMPANY);
1213f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            String title = values.getAsString(Organization.TITLE);
1214f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
121551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
121651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
121751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
121851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            insertNameLookupForOrganization(rawContactId, dataId, company, title);
121951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return dataId;
122051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
122151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
122251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        @Override
122351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1224bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean callerIsSyncAdapter) {
1225bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            String company = values.getAsString(Organization.COMPANY);
1226bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            String title = values.getAsString(Organization.TITLE);
1227bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
122851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1229fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1230fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            super.update(db, values, c, callerIsSyncAdapter);
1231fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1232fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1233fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            deleteNameLookup(dataId);
1234fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1235fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1236fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1237fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        @Override
1238fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1239fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
1240fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1241fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1242fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            int count = super.delete(db, c);
1243fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1244fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            deleteNameLookup(dataId);
1245fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            return count;
1246fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1247fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1248fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        @Override
124905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        protected int getTypeRank(int type) {
125005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            switch (type) {
125105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                case Organization.TYPE_WORK: return 0;
125205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
1253bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
1254bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                default: return 1000;
125551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
125651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
12573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
12583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        @Override
12593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        public boolean isAggregationRequired() {
12603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
12613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
12623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
12633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
126449d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
126549d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov
12663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        public EmailDataRowHandler() {
12673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
12683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
12693826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
12703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        @Override
12713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
127231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            String address = values.getAsString(Email.DATA);
1273de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
1274b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1275b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov
127631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
127731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
1278013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov            return dataId;
1279013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        }
1280013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
1281013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        @Override
12825df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
12835df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                boolean callerIsSyncAdapter) {
12845df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
12855df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
12865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            String address = values.getAsString(Email.DATA);
1287ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov
128872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            super.update(db, values, c, callerIsSyncAdapter);
128972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
129072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            deleteNameLookup(dataId);
129172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
12925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
12935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
12945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
12955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        @Override
12963d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1297b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
1298b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
12993d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
13003d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            int count = super.delete(db, c);
1301568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1302568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            deleteNameLookup(dataId);
1303568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1304568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            return count;
1305568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1306bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1307568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        @Override
1308bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        protected int getTypeRank(int type) {
1309bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            switch (type) {
1310bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1311568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1312bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1313bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1314bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                default: return 1000;
1315568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
1316bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1317bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1318bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1319bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
1320bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1321bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        public NicknameDataRowHandler() {
1322568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
1323568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1324bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1325bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        @Override
1326bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1327bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
1328bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1329bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1330bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1331bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1332b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
1333b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov            return dataId;
1334b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        }
1335bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1336bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        @Override
1337bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1338bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                boolean callerIsSyncAdapter) {
1339bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
1340bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1341bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
1342bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1343bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            super.update(db, values, c, callerIsSyncAdapter);
1344bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1345bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            deleteNameLookup(dataId);
1346bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
1347bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1348bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
1349bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1350bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        @Override
1351bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1352bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
1353bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1354bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1355bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int count = super.delete(db, c);
1356bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1357bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            deleteNameLookup(dataId);
1358bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1359bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            return count;
1360bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
1361bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1362bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1363bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
13643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
13653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        public PhoneDataRowHandler() {
13663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
1367568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
13680e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
13693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        @Override
13703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1371bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            long dataId;
1372bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1373bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
1374bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1375bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1376bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
13773d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
13783d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
13793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
1380bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1381bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            } else {
13823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
13833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            }
1384a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            return dataId;
1385a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
1386a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1387a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        @Override
1388b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
13893826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                boolean callerIsSyncAdapter) {
1390a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
1391a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1392568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
139315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
1394568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1395568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1396568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                super.update(db, values, c, callerIsSyncAdapter);
1397568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1398568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
139915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
140015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
140115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } else {
140215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                super.update(db, values, c, callerIsSyncAdapter);
140315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
140415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
140515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
140615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        @Override
140715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
140815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
140915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1410ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov
1411568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            int count = super.delete(db, c);
1412568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1413568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1414568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
1415568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
141615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return count;
1417568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1418568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1419568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        private String computeNormalizedNumber(String number, ContentValues values) {
1420568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            String normalizedNumber = null;
1421568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            if (number != null) {
142215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1423bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1424bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1425bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            return normalizedNumber;
1426bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
1427bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1428bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1429bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                String number, String normalizedNumber) {
1430bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (number != null) {
1431bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
1432bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1433bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1434bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
1435bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.MIN_MATCH,
1436bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                        PhoneNumberUtils.toCallerIDMinMatch(number));
143715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
1438568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1439568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            } else {
1440568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
1441568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=?", mSelectionArgs1);
1442568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
144315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
1444568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1445568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        @Override
1446568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        protected int getTypeRank(int type) {
1447568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            switch (type) {
1448568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
1449568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                case Phone.TYPE_WORK: return 1;
145015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
1451568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
1452568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
1453568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
14544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                case Phone.TYPE_FAX_WORK: return 6;
14557b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
14567b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov                default: return 1000;
14577b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov            }
14587b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        }
14597b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
14607b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
1461285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1462bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
1463b5a4add17815167d20a90645779df34cdf45280dFred Quintana        public GroupMembershipRowHandler() {
1464b5a4add17815167d20a90645779df34cdf45280dFred Quintana            super(GroupMembership.CONTENT_ITEM_TYPE);
1465285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
14661ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana
1467d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        @Override
1468b5a4add17815167d20a90645779df34cdf45280dFred Quintana        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1469b5a4add17815167d20a90645779df34cdf45280dFred Quintana            resolveGroupSourceIdInValues(rawContactId, db, values, true);
1470285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1471285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            updateVisibility(rawContactId);
1472285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            return dataId;
14731129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        }
1474bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
1475b5a4add17815167d20a90645779df34cdf45280dFred Quintana        @Override
1476b5a4add17815167d20a90645779df34cdf45280dFred Quintana        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1477285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                boolean callerIsSyncAdapter) {
1478b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1479bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
14801a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            super.update(db, values, c, callerIsSyncAdapter);
14811a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            updateVisibility(rawContactId);
1482b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        }
14831a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
14843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        @Override
1485bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1486bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
14873826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            int count = super.delete(db, c);
14883826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateVisibility(rawContactId);
14893826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return count;
14903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
1491b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1492b5a4add17815167d20a90645779df34cdf45280dFred Quintana        private void updateVisibility(long rawContactId) {
1493bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
1494bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            if (contactId != 0) {
1495bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
1496bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            }
1497bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
1498bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
1499bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1500bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                ContentValues values, boolean isInsert) {
1501bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1502b5a4add17815167d20a90645779df34cdf45280dFred Quintana            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1503bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1504b5a4add17815167d20a90645779df34cdf45280dFred Quintana                throw new IllegalArgumentException(
1505b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
15061129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1507d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            }
15088ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov
1509bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1510285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                if (isInsert) {
1511b5a4add17815167d20a90645779df34cdf45280dFred Quintana                    throw new IllegalArgumentException(
1512d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1513d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1514a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                } else {
1515a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    return;
1516d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                }
1517a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            }
1518a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1519a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            if (containsGroupSourceId) {
1520a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1521d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
1522d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mInsertedRawContacts.get(rawContactId));
1523a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1524a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1525d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            }
1526a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1527a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1528b5a4add17815167d20a90645779df34cdf45280dFred Quintana        @Override
1529b5a4add17815167d20a90645779df34cdf45280dFred Quintana        public boolean isAggregationRequired() {
1530d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return false;
1531b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
15329d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana    }
15339d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana
15349d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana    public class PhotoDataRowHandler extends DataRowHandler {
15359d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana
1536b5a4add17815167d20a90645779df34cdf45280dFred Quintana        public PhotoDataRowHandler() {
1537b5a4add17815167d20a90645779df34cdf45280dFred Quintana            super(Photo.CONTENT_ITEM_TYPE);
1538d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        }
1539b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1540b5a4add17815167d20a90645779df34cdf45280dFred Quintana        @Override
1541a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1542a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1543a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1544a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1545d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            }
1546b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return dataId;
1547a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1548b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1549a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        @Override
1550a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1551285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                boolean callerIsSyncAdapter) {
1552285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1553285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            super.update(db, values, c, callerIsSyncAdapter);
1554cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
155581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        }
155681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
155781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        @Override
155881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
155981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
156081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            int count = super.delete(db, c);
156181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1562cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov            return count;
1563568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
156451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
15653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        @Override
15663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        public boolean isAggregationRequired() {
15673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
15683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
156951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
157051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1571f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    /**
15723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
15733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * and source id) to group row id.
15746d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov     */
15756d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov    public class GroupIdCacheEntry {
15763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        String accountType;
15773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        String accountName;
15783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        String sourceId;
15793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        long groupId;
15803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
15814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1582de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1583bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
15841129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1585b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private NameSplitter mNameSplitter;
1586f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private NameLookupBuilder mNameLookupBuilder;
1587f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1588f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    // We will use this much memory (in bits) to optimize the nickname cluster lookup
1589f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private static final int NICKNAME_BLOOM_FILTER_SIZE = 0x1FFF;   // =long[128]
1590a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private BitSet mNicknameBloomFilter;
1591a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
159235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private HashMap<String, SoftReference<String[]>> mNicknameClusterCache = Maps.newHashMap();
1593a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
159435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private PostalSplitter mPostalSplitter;
1595b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov
159635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    // We don't need a soft cache for groups - the assumption is that there will only
159735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    // be a small number of contact groups. The cache is keyed off source id.  The value
1598d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    // is a list of groups with this group id.
1599d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
16006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
16016bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private ContactAggregator mContactAggregator;
16026bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
16035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1604dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1605f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private ContentValues mValues = new ContentValues();
1606a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private CharArrayBuffer mCharArrayBuffer = new CharArrayBuffer(128);
1607a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1608a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private volatile CountDownLatch mAccessLatch;
16095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
16105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private HashMap<Long, Account> mInsertedRawContacts = Maps.newHashMap();
1611f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1612f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private HashSet<Long> mDirtyRawContacts = Sets.newHashSet();
1613a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1614a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1615a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private boolean mVisibleTouched = false;
1616a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1617f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private boolean mSyncToNetwork;
1618f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1619a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    @Override
1620a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    public boolean onCreate() {
1621a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        super.onCreate();
1622ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1623f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final Context context = getContext();
1624f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
1625ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mGlobalSearchSupport = new GlobalSearchSupport(this);
1626ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1627ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mContactAggregator = new ContactAggregator(this, mDbHelper);
1628eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
16295aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
163043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
1631eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
1632eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        mSetPrimaryStatement = db.compileStatement(
1633eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                "UPDATE " + Tables.DATA +
163482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
163582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
16361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                "   AND " + Data.RAW_CONTACT_ID + "=?");
16371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
16381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        mSetSuperPrimaryStatement = db.compileStatement(
1639a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                "UPDATE " + Tables.DATA +
164081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1641f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1642a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1643a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                        "SELECT " + RawContacts._ID +
16447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        " FROM " + Tables.RAW_CONTACTS +
16457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
16467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                                "SELECT " + RawContacts.CONTACT_ID +
16477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                                " FROM " + Tables.RAW_CONTACTS +
1648de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1649a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1650a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        mContactsLastTimeContactedUpdate = db.compileStatement(
1651a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                "UPDATE " + Tables.CONTACTS +
1652e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                " SET " + Contacts.LAST_TIME_CONTACTED + "=? " +
1653e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                "WHERE " + Contacts._ID + "=?");
1654e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1655e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mRawContactDisplayNameUpdate = db.compileStatement(
1656e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                "UPDATE " + Tables.RAW_CONTACTS +
1657e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                " SET " + RawContactsColumns.DISPLAY_NAME + "=?,"
1658e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        + RawContactsColumns.DISPLAY_NAME_SOURCE + "=?" +
1659e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                " WHERE " + RawContacts._ID + "=?");
1660e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1661e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mLastStatusUpdate = db.compileStatement(
1662e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                "UPDATE " + Tables.CONTACTS +
1663e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1664e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        "(SELECT " + DataColumns.CONCRETE_ID +
16657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        " FROM " + Tables.STATUS_UPDATES +
1666e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        " JOIN " + Tables.DATA +
1667f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1668f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1669e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        " JOIN " + Tables.RAW_CONTACTS +
1670f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1671f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1672f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
1673e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
1674e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                                + StatusUpdates.STATUS +
1675e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        " LIMIT 1)" +
1676e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1677e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1678fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov        final Locale locale = Locale.getDefault();
1679fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov        mNameSplitter = new NameSplitter(
1680e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                context.getString(com.android.internal.R.string.common_name_prefixes),
1681e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                context.getString(com.android.internal.R.string.common_last_name_prefixes),
1682e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                context.getString(com.android.internal.R.string.common_name_suffixes),
1683e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                context.getString(com.android.internal.R.string.common_name_conjunctions),
1684e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                locale);
1685e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
1686e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mPostalSplitter = new PostalSplitter(locale);
1687e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1688e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mNameLookupInsert = db.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1689e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1690e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1691e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                + ") VALUES (?,?,?,?)");
1692fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov        mNameLookupDelete = db.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1693fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1694e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1695e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mStatusUpdateInsert = db.compileStatement(
1696e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1697f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
1698f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
1699e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        + StatusUpdates.STATUS_RES_PACKAGE + ","
1700f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
1701f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
1702e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                " VALUES (?,?,?,?,?)");
1703e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1704f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mStatusUpdateReplace = db.compileStatement(
1705f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1706e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        + StatusUpdatesColumns.DATA_ID + ", "
1707f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
1708f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
1709f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
1710f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
1711035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + StatusUpdates.STATUS_LABEL + ")" +
1712f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1713e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
17147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        mStatusUpdateAutoTimestamp = db.compileStatement(
17157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "UPDATE " + Tables.STATUS_UPDATES +
17167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
1717d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
17186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
17196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
17206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
17216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        mStatusAttributionUpdate = db.compileStatement(
1722d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
1723de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
17246bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
17256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
17266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1727a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1728a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        mStatusUpdateDelete = db.compileStatement(
1729f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1730f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1731dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1732a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        mDataRowHandlers = new HashMap<String, DataRowHandler>();
1733a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1734dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
1735f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
1736f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
1737f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
1738f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
1739e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
17407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
17413d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
17423d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
1743f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
17443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
17453d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                new StructuredPostalRowHandler(mPostalSplitter));
1746f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
1747f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
1748f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
1749f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
1750f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            importLegacyContactsAsync();
1751f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
1752285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1753285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        verifyAccounts();
1754d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov
1755f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1756dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
1757dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mMimeTypeIdStructuredName = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE);
1758dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mMimeTypeIdOrganization = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE);
1759dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mMimeTypeIdNickname = mDbHelper.getMimeTypeId(Nickname.CONTENT_ITEM_TYPE);
1760dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mMimeTypeIdPhone = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
1761dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        preloadNicknameBloomFilter();
1762dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return (db != null);
1763dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
17643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
1765023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov    protected void verifyAccounts() {
1766a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
1767a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
1768dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1769dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1770dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    /* Visible for testing */
1771dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    @Override
1772dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1773dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return ContactsDatabaseHelper.getInstance(context);
1774dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1775dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1776dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    /* package */ NameSplitter getNameSplitter() {
1777dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return mNameSplitter;
1778dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1779dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1780dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    protected boolean isLegacyContactImportNeeded() {
1781dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1782dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return prefs.getInt(PREF_CONTACTS_IMPORTED, 0) < PREF_CONTACTS_IMPORT_VERSION;
1783dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1784dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1785dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    protected LegacyContactImporter getLegacyContactImporter() {
1786dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return new LegacyContactImporter(getContext(), this);
1787dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1788dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1789dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    /**
1790dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * Imports legacy contacts in a separate thread.  As long as the import process is running
1791dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * all other access to the contacts is blocked.
1792dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     */
1793dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void importLegacyContactsAsync() {
1794dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mAccessLatch = new CountDownLatch(1);
1795dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1796dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Thread importThread = new Thread("LegacyContactImport") {
1797dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            @Override
1798dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            public void run() {
1799dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (importLegacyContacts()) {
1800dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    // TODO aggregate all newly added raw contacts
1801dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1802dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    /*
1803dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                     * When the import process is done, we can unlock the provider and
1804dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                     * start aggregating the imported contacts asynchronously.
1805dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                     */
1806dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mAccessLatch.countDown();
1807dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mAccessLatch = null;
1808dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
1809dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1810dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        };
1811dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1812dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        importThread.start();
1813dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1814dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1815dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private boolean importLegacyContacts() {
1816dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        LegacyContactImporter importer = getLegacyContactImporter();
1817dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (importLegacyContacts(importer)) {
1818dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1819dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Editor editor = prefs.edit();
1820a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            editor.putInt(PREF_CONTACTS_IMPORTED, PREF_CONTACTS_IMPORT_VERSION);
1821a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            editor.commit();
1822a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            return true;
1823a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        } else {
1824a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            return false;
1825a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1826f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    }
1827a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1828de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    /* Visible for testing */
1829de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
183067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        boolean aggregatorEnabled = mContactAggregator.isEnabled();
1831de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mContactAggregator.setEnabled(false);
183220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
1833de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            importer.importContacts();
1834de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            mContactAggregator.setEnabled(aggregatorEnabled);
1835de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            return true;
1836b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        } catch (Throwable e) {
1837de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
1838de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov           return false;
1839508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
1840de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    }
1841de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
1842de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    /**
1843de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov     * Wipes all data from the contacts database.
1844de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov     */
18454097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    /* package */ void wipeData() {
1846b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
1847de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    }
1848a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1849a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    /**
1850d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov     * While importing and aggregating contacts, this content provider will
1851f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana     * block all attempts to change contacts data. In particular, it will hold
1852d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1853a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * processes waiting to write to the provider are unblocked and can proceed
1854d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov     * to compete for the database transaction monitor.
1855a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
18564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private void waitForAccess() {
18574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        CountDownLatch latch = mAccessLatch;
1858ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        if (latch != null) {
18598ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov            while (true) {
1860d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                try {
1861d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                    latch.await();
18629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    mAccessLatch = null;
186320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    return;
186420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                } catch (InterruptedException e) {
1865f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    Thread.currentThread().interrupt();
186620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
186720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
1868de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
1869de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    }
1870f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov
1871f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    @Override
1872de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
1873de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        waitForAccess();
1874f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        return super.insert(uri, values);
1875f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    }
1876a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1877d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    @Override
1878f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
1879d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        waitForAccess();
188088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
188120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
188220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1883de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
188420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
188520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        waitForAccess();
188620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
188720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
188820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
188988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    @Override
189088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
189188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            throws OperationApplicationException {
189220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        waitForAccess();
1893f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        return super.applyBatch(operations);
189488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    }
189588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
18964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov    @Override
1897f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    protected void onBeginTransaction() {
18984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        if (VERBOSE_LOGGING) {
1899f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov            Log.v(TAG, "onBeginTransaction");
190020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
190120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        super.onBeginTransaction();
190220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mContactAggregator.clearPendingAggregations();
190320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        clearTransactionalChanges();
190420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
1905f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov
190620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private void clearTransactionalChanges() {
190720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mInsertedRawContacts.clear();
190820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mUpdatedRawContacts.clear();
190920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mUpdatedSyncStates.clear();
191020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mDirtyRawContacts.clear();
191120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
191220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
191320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    @Override
191420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    protected void beforeTransactionCommit() {
19157a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana
191620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (VERBOSE_LOGGING) {
191720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            Log.v(TAG, "beforeTransactionCommit");
191820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
1919a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        super.beforeTransactionCommit();
1920d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        flushTransactionalChanges();
192120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mContactAggregator.aggregateInTransaction(mDb);
192220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (mVisibleTouched) {
192320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mVisibleTouched = false;
192420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mDbHelper.updateAllVisible();
192520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
192620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
1927ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1928ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private void flushTransactionalChanges() {
1929f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (VERBOSE_LOGGING) {
1930f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            Log.v(TAG, "flushTransactionChanges");
1931f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
1932f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1933e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        for (long rawContactId : mInsertedRawContacts.keySet()) {
1934ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            updateRawContactDisplayName(mDb, rawContactId);
1935ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            mContactAggregator.onRawContactInsert(mDb, rawContactId);
1936f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
193767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
1938f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (!mDirtyRawContacts.isEmpty()) {
193967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            mSb.setLength(0);
1940f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1941ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            appendIds(mSb, mDirtyRawContacts);
1942dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            mSb.append(")");
1943dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            mDb.execSQL(mSb.toString());
1944dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1945dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1946f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
1947f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mSb.setLength(0);
194873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
194973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            appendIds(mSb, mUpdatedRawContacts);
1950f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mSb.append(")");
1951ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey            mDb.execSQL(mSb.toString());
1952dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1953dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1954dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
1955dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            long id = entry.getKey();
1956dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            mDbHelper.getSyncState().update(mDb, id, entry.getValue());
1957dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1958dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1959dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        clearTransactionalChanges();
1960dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1961dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1962dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    /**
1963dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * Appends comma separated ids.
1964dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param ids Should not be empty
1965dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     */
1966dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void appendIds(StringBuilder sb, HashSet<Long> ids) {
1967dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        for (long id : ids) {
1968892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            sb.append(id).append(',');
1969892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov        }
1970892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov
1971892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1972892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov    }
1973d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov
1974892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov    @Override
1975dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    protected void notifyChange() {
1976892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov        notifyChange(mSyncToNetwork);
1977892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov        mSyncToNetwork = false;
1978dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1979dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1980dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    protected void notifyChange(boolean syncToNetwork) {
1981f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
19821a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                syncToNetwork);
1983ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey    }
1984ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
1985ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey    private boolean isNewRawContact(long rawContactId) {
1986ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return mInsertedRawContacts.containsKey(rawContactId);
1987ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
19885aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
1989e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    private DataRowHandler getDataRowHandler(final String mimeType) {
19905aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        DataRowHandler handler = mDataRowHandlers.get(mimeType);
19911a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (handler == null) {
19921a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            handler = new CustomDataRowHandler(mimeType);
1993e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            mDataRowHandlers.put(mimeType, handler);
19941a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
1995e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return handler;
1996e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
1997e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
1998ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    @Override
199982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
20001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (VERBOSE_LOGGING) {
200182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
200282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
20030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
20044dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        final boolean callerIsSyncAdapter =
20054dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
20060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
200782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final int match = sUriMatcher.match(uri);
20084dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        long id = 0;
20094dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
20104dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        switch (match) {
20114dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            case SYNCSTATE:
20121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                id = mDbHelper.getSyncState().insert(mDb, values);
20131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
2014dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2015dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            case CONTACTS: {
201682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                insertContact(values);
2017f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                break;
20182526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            }
2019dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2020dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            case RAW_CONTACTS: {
2021dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                id = insertRawContact(uri, values);
20222526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
20232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                break;
20241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
2025dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2026dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            case RAW_CONTACTS_DATA: {
20270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
20280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                id = insertData(values, callerIsSyncAdapter);
20290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
20300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                break;
2031dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
2032dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2033dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            case DATA: {
20342a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                id = insertData(values, callerIsSyncAdapter);
2035dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                mSyncToNetwork |= !callerIsSyncAdapter;
20362a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                break;
2037f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            }
2038f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2039f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            case GROUPS: {
2040f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2041f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2042f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                break;
2043f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            }
20442526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
20452526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            case SETTINGS: {
20462526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                id = insertSettings(uri, values);
20472526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
20482526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                break;
20492526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            }
20502526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
20512526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            case STATUS_UPDATES: {
2052dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                id = insertStatusUpdate(values);
20532526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                break;
20542526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            }
2055dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
20562526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            default:
20572526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSyncToNetwork = true;
2058dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                return mLegacyApiSupport.insert(uri, values);
20592526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        }
20602526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
20612526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        if (id < 0) {
20622526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            return null;
20632526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        }
20642526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
2065dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        return ContentUris.withAppendedId(uri, id);
20662526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    }
20672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
2068dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton    /**
2069dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton     * If account is non-null then store it in the values. If the account is already
20701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     * specified in the values then it must be consistent with the account, if it is non-null.
207182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * @param uri the ContentValues to read from and update
20722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov     * @param values the explicitly provided Account
20732526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov     * @return false if the parameters are inconsistent
2074dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton     */
207570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    private boolean resolveAccount(Uri uri, ContentValues values) {
2076f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
207770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
20781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
20791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
2080de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            accountName = null;
20812526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            accountType = null;
20824394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov        }
20831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
208467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
20855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2086e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
20871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (TextUtils.isEmpty(valueAccountName) && TextUtils.isEmpty(valueAccountType)) {
20881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            values.put(RawContacts.ACCOUNT_NAME, accountName);
20891f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            values.put(RawContacts.ACCOUNT_TYPE, accountType);
20901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
20911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (accountName != null && !accountName.equals(valueAccountName)) {
209231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                return false;
209331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
209431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
20951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (accountType != null && !accountType.equals(valueAccountType)) {
20961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return false;
209782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            }
2098a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2099a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            accountName = valueAccountName;
2100a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            accountType = valueAccountType;
2101a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2102a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2103a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
2104a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mAccount = null;
210582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            return true;
2106a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2107a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
210882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (mAccount == null
210982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                || !mAccount.name.equals(accountName)
211082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
211182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mAccount = new Account(accountName, accountType);
211282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
2113a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
211482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        return true;
211582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
2116aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori
2117aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori    /**
21181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     * Inserts an item in the contacts table
2119a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov     *
2120a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov     * @param values the values for the new row
2121a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov     * @return the row ID of the newly created row
2122e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov     */
21230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private long insertContact(ContentValues values) {
212482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
212582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
21260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
21270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    /**
21280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     * Inserts an item in the contacts table
21290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     *
21300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     * @param uri the values for the new row
21310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
21320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     * @return the row ID of the newly created row
21330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     */
21340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private long insertRawContact(Uri uri, ContentValues values) {
21350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        mValues.clear();
21360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        mValues.putAll(values);
21370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2138a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
213978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov        if (!resolveAccount(uri, mValues)) {
214082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            return -1;
214182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
214278fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov
214378fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
2144a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
214578fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
214678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov        }
2147e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
2148e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2149bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId);
2150a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2151f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2152a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mInsertedRawContacts.put(rawContactId, mAccount);
2153a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2154a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return rawContactId;
21551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
21561f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
21574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    /**
2158de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov     * Inserts an item in the data table
2159bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov     *
2160b5a4add17815167d20a90645779df34cdf45280dFred Quintana     * @param values the values for the new row
2161b5a4add17815167d20a90645779df34cdf45280dFred Quintana     * @return the row ID of the newly created row
2162b5a4add17815167d20a90645779df34cdf45280dFred Quintana     */
2163f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2164f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        long id = 0;
2165508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        mValues.clear();
2166508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        mValues.putAll(values);
216735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2168b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
216935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2170b5a4add17815167d20a90645779df34cdf45280dFred Quintana        // Replace package with internal mapping
2171b5a4add17815167d20a90645779df34cdf45280dFred Quintana        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2172b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (packageName != null) {
2173b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2174b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        }
2175b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mValues.remove(Data.RES_PACKAGE);
2176cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2177cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        // Replace mimetype with internal mapping
2178cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2179cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2180cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2181d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        }
2182d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
2183dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
21846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        mValues.remove(Data.MIMETYPE);
21856bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
21869fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        DataRowHandler rowHandler = getDataRowHandler(mimeType);
21872e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey        id = rowHandler.insert(mDb, rawContactId, mValues);
21882e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey        if (!callerIsSyncAdapter) {
21892e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            setRawContactDirty(rawContactId);
2190fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov        }
2191fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov        mUpdatedRawContacts.add(rawContactId);
21922e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
21932e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey        if (rowHandler.isAggregationRequired()) {
21942e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            triggerAggregation(rawContactId);
2195dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
21962e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey        return id;
21972e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey    }
21989fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
21999fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann    private void triggerAggregation(long rawContactId) {
22009fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        if (!mContactAggregator.isEnabled()) {
22019fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            return;
22029fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        }
22039fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
2204a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
22059fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        switch (aggregationMode) {
22069fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case RawContacts.AGGREGATION_MODE_DISABLED:
22079fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                break;
22089fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
22099fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case RawContacts.AGGREGATION_MODE_DEFAULT: {
22109fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                mContactAggregator.markForAggregation(rawContactId);
22119fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                break;
22129fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
221360de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann
22149fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
22159fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                long contactId = mDbHelper.getContactId(rawContactId);
22169fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
22179fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (contactId != 0) {
22189fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    mContactAggregator.updateAggregateData(contactId);
22199fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
2220dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                break;
22219fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
22229fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
22239fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
22249fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                long contactId = mDbHelper.getContactId(rawContactId);
22259fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
22269fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                break;
22279fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
22289fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        }
22299fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann    }
22309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
22312971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana    /**
22322971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana     * Returns the group id of the group with sourceId and the same account as rawContactId.
2233fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov     * If the group doesn't already exist then it is first created,
2234fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov     * @param db SQLiteDatabase to use for this operation
2235e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong     * @param rawContactId the contact this group is associated with
22362971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana     * @param sourceId the sourceIf of the group to query or create
22372971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana     * @return the group id of the existing or created group
22382971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
2239fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov     * @throws IllegalStateException if a group needs to be created but the creation failed
2240fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov     */
2241fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
22422971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            Account account) {
22432971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
22442971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        if (account == null) {
22452971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            mSelectionArgs1[0] = String.valueOf(rawContactId);
22462971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
22472971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
22482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            try {
22495ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                if (c.moveToFirst()) {
22502971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
2251fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                    String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
2252fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2253508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                        account = new Account(accountName, accountType);
2254508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                    }
225520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
2256f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            } finally {
2257944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                c.close();
2258f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            }
225920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
226020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
226148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        if (account == null) {
226248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            throw new IllegalArgumentException("if the groupmembership only "
226348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                    + "has a sourceid the the contact must be associated with "
226448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                    + "an account");
2265508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
2266f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
22674da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
22684da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        if (entries == null) {
2269ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            entries = new ArrayList<GroupIdCacheEntry>(1);
2270ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            mGroupIdCache.put(sourceId, entries);
2271ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
2272f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
22735aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        int count = entries.size();
22742971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        for (int i = 0; i < count; i++) {
22752971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            GroupIdCacheEntry entry = entries.get(i);
22762971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) {
22772971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return entry.groupId;
22782971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
2279e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
22802971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
22812971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        GroupIdCacheEntry entry = new GroupIdCacheEntry();
22825aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        entry.accountName = account.name;
22832971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        entry.accountType = account.type;
22842971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        entry.sourceId = sourceId;
22852971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        entries.add(0, entry);
22862971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
228781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        // look up the group that contains this sourceId and has the same account name and type
2288f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        // as the contact refered to by rawContactId
228981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
22902971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2291508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                new String[]{sourceId, account.name, account.type}, null, null, null);
2292508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        try {
2293eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            if (c.moveToFirst()) {
229443880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                entry.groupId = c.getLong(0);
2295e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            } else {
2296eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                ContentValues groupValues = new ContentValues();
2297eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                groupValues.put(Groups.ACCOUNT_NAME, account.name);
229882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
22990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                groupValues.put(Groups.SOURCE_ID, sourceId);
23001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
23011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                if (groupId < 0) {
230281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    throw new IllegalStateException("unable to create a new group with "
230381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                            + "this sourceid: " + groupValues);
23043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
230581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                entry.groupId = groupId;
2306508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
23074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        } finally {
23084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.close();
23091c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov        }
2310ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2311b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return entry.groupId;
231294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
2313de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
231494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    private interface DisplayNameQuery {
231594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        public static final String RAW_SQL =
231694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                "SELECT "
231794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                        + DataColumns.MIMETYPE_ID + ","
2318f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        + Data.IS_PRIMARY + ","
2319de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                        + Data.DATA1 + ","
232094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                        + Organization.TITLE +
232194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                " FROM " + Tables.DATA +
232294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                " WHERE " + Data.RAW_CONTACT_ID + "=?" +
2323f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        " AND (" + Data.DATA1 + " NOT NULL OR " +
2324de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                                Organization.TITLE + " NOT NULL)";
232594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
232694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        public static final int MIMETYPE = 0;
23271a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        public static final int IS_PRIMARY = 1;
232894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        public static final int DATA = 2;
232994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        public static final int TITLE = 3;
233094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
23315aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
2332e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    /**
23331a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey     * Updates a raw contact display name based on data rows, e.g. structured name,
2334e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey     * organization, email etc.
2335e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey     */
2336e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    private void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
2337dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        String bestDisplayName = null;
233896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
2339cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
234096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = String.valueOf(rawContactId);
234196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        Cursor c = db.rawQuery(DisplayNameQuery.RAW_SQL, mSelectionArgs1);
2342cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2343cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2344cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                int mimeType = c.getInt(DisplayNameQuery.MIMETYPE);
2345dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2346cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // Display name is at DATA1 in all type. This is ensured in the
2347cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // constructor.
2348cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                mCharArrayBuffer.sizeCopied = 0;
2349cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                c.copyStringToBuffer(DisplayNameQuery.DATA, mCharArrayBuffer);
2350cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                if (mimeType == mMimeTypeIdOrganization && mCharArrayBuffer.sizeCopied == 0) {
23513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.TITLE, mCharArrayBuffer);
23523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                }
2353cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2354cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                if (mCharArrayBuffer.sizeCopied != 0) {
2355cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                    int source = getDisplayNameSource(mimeType);
2356fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                    if (source > bestDisplayNameSource) {
23573389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov                        bestDisplayNameSource = source;
23583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
23593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
2360f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    } else if (source == bestDisplayNameSource
236114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                            && source != DisplayNameSources.UNDEFINED) {
2362fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        if (mimeType == mMimeTypeIdStructuredName
2363fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                || c.getInt(DisplayNameQuery.IS_PRIMARY) != 0) {
2364fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                            bestDisplayNameSource = source;
236533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                            bestDisplayName = new String(mCharArrayBuffer.data, 0,
2366b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                                    mCharArrayBuffer.sizeCopied);
2367dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        }
236833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                    }
236933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                }
237033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov            }
23710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
23729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        } finally {
23739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            c.close();
23749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
23759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
23769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        setDisplayName(rawContactId, bestDisplayName, bestDisplayNameSource);
23779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
23789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
23799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int getDisplayNameSource(int mimeTypeId) {
23800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (mimeTypeId == mMimeTypeIdStructuredName) {
23810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            return DisplayNameSources.STRUCTURED_NAME;
2382dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } else if (mimeTypeId == mMimeTypeIdEmail) {
238381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            return DisplayNameSources.EMAIL;
238481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdPhone) {
2385cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return DisplayNameSources.PHONE;
2386cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdOrganization) {
2387cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return DisplayNameSources.ORGANIZATION;
2388cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdNickname) {
2389cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return DisplayNameSources.NICKNAME;
2390cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } else {
2391dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return DisplayNameSources.UNDEFINED;
2392cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2393cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
23944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2395de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    /**
2396de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
2397bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov     */
2398b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
2399b5a4add17815167d20a90645779df34cdf45280dFred Quintana        int count = 0;
2400b5a4add17815167d20a90645779df34cdf45280dFred Quintana
240135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        // Note that the query will return data according to the access restrictions,
240200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        // so we don't need to worry about deleting data we don't have permission to read.
240300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2404b5a4add17815167d20a90645779df34cdf45280dFred Quintana        try {
2405b5a4add17815167d20a90645779df34cdf45280dFred Quintana            while(c.moveToNext()) {
24061129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
2407d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2408b5a4add17815167d20a90645779df34cdf45280dFred Quintana                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2409b5a4add17815167d20a90645779df34cdf45280dFred Quintana                count += rowHandler.delete(mDb, c);
2410b5a4add17815167d20a90645779df34cdf45280dFred Quintana                if (!callerIsSyncAdapter) {
2411f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    setRawContactDirty(rawContactId);
2412f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    if (rowHandler.isAggregationRequired()) {
241300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                        triggerAggregation(rawContactId);
241435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                    }
2415b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                }
2416b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
2417b5a4add17815167d20a90645779df34cdf45280dFred Quintana        } finally {
2418b5a4add17815167d20a90645779df34cdf45280dFred Quintana            c.close();
2419b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2420b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2421b5a4add17815167d20a90645779df34cdf45280dFred Quintana        return count;
2422b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2423b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov
2424b5a4add17815167d20a90645779df34cdf45280dFred Quintana    /**
2425b5a4add17815167d20a90645779df34cdf45280dFred Quintana     * Delete a data row provided that it is one of the allowed mime types.
242635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana     */
2427d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2428dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
242900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        // Note that the query will return data according to the access restrictions,
243000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        // so we don't need to worry about deleting data we don't have permission to read.
243100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        mSelectionArgs1[0] = String.valueOf(dataId);
2432d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=?",
2433dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mSelectionArgs1, null);
2434c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
2435c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        try {
2436c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            if (!c.moveToFirst()) {
24372e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                return 0;
24382e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
24392e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
24402e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
24412e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            boolean valid = false;
2442fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
2443fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
24442e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    valid = true;
24452e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    break;
24462e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
2447dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
24482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
24492e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            if (!valid) {
24502e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                throw new IllegalArgumentException("Data type mismatch: expected "
24517d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                        + Lists.newArrayList(allowedMimeTypes));
24527d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
24537d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
24547d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            DataRowHandler rowHandler = getDataRowHandler(mimeType);
24557d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            int count = rowHandler.delete(mDb, c);
24567d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
24577d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            if (rowHandler.isAggregationRequired()) {
24587d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                triggerAggregation(rawContactId);
24597d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
24607d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            return count;
246120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2462944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong            c.close();
2463f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        }
246481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
2465f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
246681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    /**
246720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Inserts an item in the groups table
246820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2469c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
247048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        mValues.clear();
247148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        mValues.putAll(values);
247248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
247348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        if (!resolveAccount(uri, mValues)) {
2474f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            return -1;
247581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        }
2476f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
247781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        // Replace package with internal mapping
247800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
247900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        if (packageName != null) {
24807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
24815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        }
24825ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey        mValues.remove(Groups.RES_PACKAGE);
2483dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
24847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (!callerIsSyncAdapter) {
24857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mValues.put(Groups.DIRTY, 1);
24867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
248833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
24894529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
24904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
24914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mVisibleTouched = true;
2492dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2493dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
24944529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return result;
24954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov    }
2496dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2497dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private long insertSettings(Uri uri, ContentValues values) {
24984529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        final long id = mDb.insert(Tables.SETTINGS, null, values);
24997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
25017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mVisibleTouched = true;
2502ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
25035aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
2504f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        return id;
250581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
2506f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
250781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    /**
2508ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts a status update.
2509ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2510ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    public long insertStatusUpdate(ContentValues values) {
2511ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
2512ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
25134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        String customProtocol = null;
25144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov
251573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
25165aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
25175aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey            if (TextUtils.isEmpty(customProtocol)) {
251881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                throw new IllegalArgumentException(
2519f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
252081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2521ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
2522ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2523ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long rawContactId = -1;
2524127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long contactId = -1;
2525de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2526b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        mSb.setLength(0);
2527b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        if (dataId != null) {
2528b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            // Lookup the contact info for the given data row.
2529eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
2530e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            mSb.append(Tables.DATA + "." + Data._ID + "=");
2531e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            mSb.append(dataId);
253243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov        } else {
2533eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            // Lookup the data row to attach this presence update to
2534eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
2535eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            if (TextUtils.isEmpty(handle) || protocol == null) {
25369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
25379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
25389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            // TODO: generalize to allow other providers to match against email
25409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
254172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
2542bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (matchEmail) {
254372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
2544d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2545d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2546d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                // the raw_contacts table.
254781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
254881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2549f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                // column - Data.DATA1
255081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (")
255100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                        .append(mMimeTypeIdEmail)
255200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                        .append(",")
255300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                        .append(mMimeTypeIdIm)
25544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                        .append(")" + " AND " + Data.DATA1 + "=");
25554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                DatabaseUtils.appendEscapedSQLString(mSb, handle);
25569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                mSb.append(" AND ((" + DataColumns.MIMETYPE_ID + "=")
25579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                        .append(mMimeTypeIdIm)
25589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                        .append(" AND " + Im.PROTOCOL + "=")
25599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                        .append(protocol);
25609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                if (customProtocol != null) {
25619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
25629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
25639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                }
25649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=")
25659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                        .append(mMimeTypeIdEmail)
25669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                        .append("))");
25679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            } else {
25689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                mSb.append(DataColumns.MIMETYPE_ID + "=")
25699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                        .append(mMimeTypeIdIm)
25709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                        .append(" AND " + Im.PROTOCOL + "=")
25719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                        .append(protocol)
25729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                        .append(" AND " + Im.DATA + "=");
25739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                DatabaseUtils.appendEscapedSQLString(mSb, handle);
25749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                if (customProtocol != null) {
25759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
25769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
25779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                }
25789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
25799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            if (values.containsKey(StatusUpdates.DATA_ID)) {
25819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=")
25829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                        .append(values.getAsLong(StatusUpdates.DATA_ID));
25839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
25849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
25859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(" AND ").append(getContactsRestrictions());
25869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        Cursor cursor = null;
25889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        try {
25899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
25909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    mSb.toString(), null, null, null,
25919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    Contacts.IN_VISIBLE_GROUP + " DESC, " + Data.RAW_CONTACT_ID);
25929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            if (cursor.moveToFirst()) {
25939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
25949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
25959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
25969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            } else {
25979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                // No contact found, return a null URI
25989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                return -1;
25999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
26009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        } finally {
26019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            if (cursor != null) {
26029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                cursor.close();
26039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
26049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
26059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
26069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (values.containsKey(StatusUpdates.PRESENCE)) {
26079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            if (customProtocol == null) {
26089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                // We cannot allow a null in the custom protocol field, because SQLite3 does not
26099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                // properly enforce uniqueness of null values
2610aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                customProtocol = "";
2611aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            }
26129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
26139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            mValues.clear();
26149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            mValues.put(StatusUpdates.DATA_ID, dataId);
26155aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2616f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            mValues.put(PresenceColumns.CONTACT_ID, contactId);
261773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
2618ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
2619ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
262073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
2621f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
262273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            }
262373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
262473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
262573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
262673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            // Insert the presence update
262773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
262873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
262973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2630ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
26311a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(StatusUpdates.STATUS)) {
26321a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            String status = values.getAsString(StatusUpdates.STATUS);
263394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
26346ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
26351129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
26366ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            if (TextUtils.isEmpty(resPackage)
2637e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && (labelResource == null || labelResource == 0)
26386ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    && protocol != null) {
26396ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                labelResource = Im.getProtocolLabelResource(protocol);
26406ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
26416ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi
26426ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
26436ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            // TODO compute the default icon based on the protocol
26446ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi
26456ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            if (TextUtils.isEmpty(status)) {
26466ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                mStatusUpdateDelete.bindLong(1, dataId);
2647ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                mStatusUpdateDelete.execute();
26486ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
26496ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
26506ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                mStatusUpdateReplace.bindLong(1, dataId);
26516ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                mStatusUpdateReplace.bindLong(2, timestamp);
26526ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 3, status);
26536ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 4, resPackage);
26546ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 5, iconResource);
26556ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 6, labelResource);
265694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mStatusUpdateReplace.execute();
265794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
265894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
2659b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                try {
2660b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
2661e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 2, status);
26621a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 3, resPackage);
26631a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 4, iconResource);
2664e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 5, labelResource);
2665e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    mStatusUpdateInsert.executeInsert();
2666e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                } catch (SQLiteConstraintException e) {
2667e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    // The row already exists - update it
2668dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long timestamp = System.currentTimeMillis();
2669dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
26704529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 2, status);
26714529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
26724529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 4, status);
26734529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
267473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
267597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 1, resPackage);
267697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 2, iconResource);
267797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 3, labelResource);
267897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
267997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    mStatusAttributionUpdate.execute();
26804529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
2681b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            }
268251bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey        }
26834529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
26844529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (contactId != -1) {
26854529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
26864529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
2687dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            mLastStatusUpdate.execute();
26884529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
26894529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
26904529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return dataId;
26914529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
26924529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
26934529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    @Override
26944529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
26954529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (VERBOSE_LOGGING) {
26964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            Log.v(TAG, "deleteInTransaction: " + uri);
2697dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2698dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        flushTransactionalChanges();
269996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final boolean callerIsSyncAdapter =
270096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
270119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final int match = sUriMatcher.match(uri);
270219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        switch (match) {
270319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            case SYNCSTATE:
2704ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
2705ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
270619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            case SYNCSTATE_ID:
270719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                String selectionWithId =
270896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
270919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                        + (selection == null ? "" : " AND (" + selection + ")");
271019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
271119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
2712ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            case CONTACTS: {
2713ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                // TODO
271419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                return 0;
271519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
271619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
271719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            case CONTACTS_ID: {
271819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                long contactId = ContentUris.parseId(uri);
271919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                return deleteContact(contactId);
272019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
2721f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
272296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker            case CONTACTS_LOOKUP:
27235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
2724f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                final List<String> pathSegments = uri.getPathSegments();
2725f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                final int segmentCount = pathSegments.size();
2726f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (segmentCount < 3) {
2727f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
2728f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
2729f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                final String lookupKey = pathSegments.get(2);
273069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
2731f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                return deleteContact(contactId);
2732f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
2733433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey
2734dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            case RAW_CONTACTS: {
2735dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                int numDeletes = 0;
2736dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2737dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
27384529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                try {
2739dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    while (c.moveToNext()) {
2740dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        final long rawContactId = c.getLong(0);
2741dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        numDeletes += deleteRawContact(rawContactId, callerIsSyncAdapter);
2742dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    }
2743dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                } finally {
2744dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    c.close();
2745dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2746dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return numDeletes;
2747dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2748dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2749dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            case RAW_CONTACTS_ID: {
2750dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                final long rawContactId = ContentUris.parseId(uri);
2751dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteRawContact(rawContactId, callerIsSyncAdapter);
2752dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2753dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2754dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            case DATA: {
2755dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2756433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2757dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        callerIsSyncAdapter);
2758285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
27592b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov
2760285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            case DATA_ID:
2761f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            case PHONES_ID:
2762f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            case EMAILS_ID:
2763f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            case POSTALS_ID: {
2764f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                long dataId = ContentUris.parseId(uri);
2765f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
276678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
2767f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
2768f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
2769f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
277019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            case GROUPS_ID: {
2771d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2772d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
277319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
27745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
27755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case GROUPS: {
277633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                int numDeletes = 0;
277733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2778321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
2779f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                try {
278020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    while (c.moveToNext()) {
278120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
278220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    }
27835ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                } finally {
278420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    c.close();
278520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
278620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (numDeletes > 0) {
278720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    mSyncToNetwork |= !callerIsSyncAdapter;
278820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
2789b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return numDeletes;
279020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
279120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
279297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            case SETTINGS: {
279397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
279497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                return deleteSettings(uri, selection, selectionArgs);
279597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            }
279697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
2797653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            case STATUS_UPDATES: {
279820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
2799653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
2800653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
2801f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            default: {
2802f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                mSyncToNetwork = true;
2803653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
2804653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
2805f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        }
280620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
2807653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
2808653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
280920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mGroupIdCache.clear();
281020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
2811653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
281220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
281320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
2814f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                + groupId, null);
2815653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
2816653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
2817321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            if (callerIsSyncAdapter) {
2818653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
2819f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            } else {
2820a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                mValues.clear();
2821d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mValues.put(Groups.DELETED, 1);
2822813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                mValues.put(Groups.DIRTY, 1);
2823813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
2824813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
2825a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        } finally {
2826321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            mVisibleTouched = true;
2827321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
28288c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
2829dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
28308c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2831b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
28328c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mVisibleTouched = true;
28338c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
28348c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
28358c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
28368c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int deleteContact(long contactId) {
2837dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
28388c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                RawContacts.CONTACT_ID + "=" + contactId, null, null, null, null);
28398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
28408c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (c.moveToNext()) {
28418c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long rawContactId = c.getLong(0);
28428c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                markRawContactAsDeleted(rawContactId);
28438c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
28448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
28458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            c.close();
28468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
2847dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2848dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2849d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
28508c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
2851b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    public int deleteRawContact(long rawContactId, boolean callerIsSyncAdapter) {
2852d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
2853b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (callerIsSyncAdapter) {
2854d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2855b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            return mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
2856d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        } else {
2857b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2858d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            return markRawContactAsDeleted(rawContactId);
2859b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        }
2860d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    }
2861d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2862d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
28638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov      // delete from both tables: presence and status_updates
2864d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov      // TODO should account type/name be appended to the where clause?
2865d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov      if (VERBOSE_LOGGING) {
2866d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov          Log.v(TAG, "deleting data from status_updates for " + selection);
28678c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov      }
2868c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
28698c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov          selectionArgs);
2870c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
2871c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey    }
28724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov
287397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov    private int markRawContactAsDeleted(long rawContactId) {
287497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mSyncToNetwork = true;
28758c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
2876dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mValues.clear();
2877dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mValues.put(RawContacts.DELETED, 1);
2878dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2879dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2880dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mValues.putNull(RawContacts.CONTACT_ID);
2881dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mValues.put(RawContacts.DIRTY, 1);
2882dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues);
2883dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2884dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2885dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    @Override
2886dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2887dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs) {
2888dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (VERBOSE_LOGGING) {
2889dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2890dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
28918c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
28928c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
28938c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
2894b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final int match = sUriMatcher.match(uri);
28958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (match == SYNCSTATE_ID && selection == null) {
2896b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long rowId = ContentUris.parseId(uri);
28978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
2898b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mUpdatedSyncStates.put(rowId, data);
28998c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            return 1;
2900b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        }
29018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        flushTransactionalChanges();
2902b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final boolean callerIsSyncAdapter =
29038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
29048c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        switch(match) {
29059b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            case SYNCSTATE:
29066e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori                return mDbHelper.getSyncState().update(mDb, values,
29079b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                        appendAccountToSelection(uri, selection), selectionArgs);
29089b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
29099b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            case SYNCSTATE_ID: {
29109b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                selection = appendAccountToSelection(uri, selection);
29119b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                String selectionWithId =
29129b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2913f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                        + (selection == null ? "" : " AND (" + selection + ")");
2914d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2915127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                        selectionWithId, selectionArgs);
2916127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
29170c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
29180c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            case CONTACTS: {
291980c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov                count = updateContactOptions(values, selection, selectionArgs);
2920ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov                break;
2921ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov            }
29220c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
29230c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            case CONTACTS_ID: {
29240c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                count = updateContactOptions(ContentUris.parseId(uri), values);
29250c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                break;
29260c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            }
29270c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
2928b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case CONTACTS_LOOKUP:
2929127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
29300c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                final List<String> pathSegments = uri.getPathSegments();
29314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                final int segmentCount = pathSegments.size();
29324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                if (segmentCount < 3) {
29330c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
29344da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                }
29354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                final String lookupKey = pathSegments.get(2);
29360c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
29376bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov                count = updateContactOptions(contactId, values);
29386bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov                break;
29390c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            }
29400c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
29410c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
29420c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                final String rawContactId = uri.getPathSegments().get(1);
2943127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
2944127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    + (selection == null ? "" : " AND " + selection);
29453389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov
294669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
294769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov
294869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                break;
294969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov            }
2950dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
2951bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            case DATA: {
2952bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2953127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                        selectionArgs, callerIsSyncAdapter);
2954127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                if (count > 0) {
2955127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    mSyncToNetwork |= !callerIsSyncAdapter;
2956127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                }
2957b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2958b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
295970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
2960bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case DATA_ID:
29613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            case PHONES_ID:
29623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            case EMAILS_ID:
2963bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case POSTALS_ID: {
2964f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
2965e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                if (count > 0) {
2966627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mSyncToNetwork |= !callerIsSyncAdapter;
296749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                }
296870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                break;
296970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
2970dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2971743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            case RAW_CONTACTS: {
2972743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                selection = appendAccountToSelection(uri, selection);
2973743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                count = updateRawContacts(values, selection, selectionArgs);
2974743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                break;
2975e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
2976743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
2977743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
2978743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
2979743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (selection != null) {
2980743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
298148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
2982627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + " AND(" + selection + ")", selectionArgs);
2983743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                } else {
2984627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
2985627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1);
2986627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
298770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                break;
298870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
298933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
2990e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            case GROUPS: {
2991e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
2992e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                        selectionArgs, callerIsSyncAdapter);
2993e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                if (count > 0) {
2994e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mSyncToNetwork |= !callerIsSyncAdapter;
2995e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
2996e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                break;
2997e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
2998e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
2999e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            case GROUPS_ID: {
3000e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                long groupId = ContentUris.parseId(uri);
3001e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
3002e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
3003e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
3004e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                count = updateGroups(uri, values, selectionWithId, selectionArgs,
3005e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                        callerIsSyncAdapter);
3006e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                if (count > 0) {
3007e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mSyncToNetwork |= !callerIsSyncAdapter;
3008e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
3009e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                break;
3010e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
3011e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
3012e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3013e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                count = updateAggregationException(mDb, values);
3014e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                break;
3015e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
3016e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
3017d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case SETTINGS: {
3018d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                count = updateSettings(uri, values, selection, selectionArgs);
3019d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3020d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
30214458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            }
3022e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
3023e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            case STATUS_UPDATES: {
302433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                count = updateStatusUpdate(uri, values, selection, selectionArgs);
302533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                break;
3026e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
302733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
302833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            default: {
302933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                mSyncToNetwork = true;
303033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
303169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov            }
303269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        }
303369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov
303433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov        return count;
303533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov    }
303669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov
303769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
303833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov        String[] selectionArgs) {
303933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov        // update status_updates table, if status is provided
304033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov        // TODO should account type/name be appended to the where clause?
304133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov        int updateCount = 0;
304233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
304333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov        if (settableValues.size() > 0) {
304433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov          updateCount = mDb.update(Tables.STATUS_UPDATES,
304533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    settableValues,
304633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    getWhereClauseForStatusUpdatesTable(selection),
3047bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                    selectionArgs);
304833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov        }
3049e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
3050bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        // now update the Presence table
305133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov        settableValues = getSettableColumnsForPresenceTable(values);
305233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov        if (settableValues.size() > 0) {
3053e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov          updateCount = mDb.update(Tables.PRESENCE, settableValues,
3054e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    selection, selectionArgs);
3055e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        }
305670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
305770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        // potentially get updated in this method.
305870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        return updateCount;
305970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
306073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
30613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    /**
30623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov     * Build a where clause to select the rows to be updated in status_updates table.
30633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov     */
30643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private String getWhereClauseForStatusUpdatesTable(String selection) {
30653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mSb.setLength(0);
30663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
3067afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        mSb.append(selection);
306870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mSb.append(")");
3069619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return mSb.toString();
30703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
30713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
30723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
30733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mValues.clear();
30743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
30753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            StatusUpdates.STATUS);
30763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
30773826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            StatusUpdates.STATUS_TIMESTAMP);
30783826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
30793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            StatusUpdates.STATUS_RES_PACKAGE);
30803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
30813826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            StatusUpdates.STATUS_LABEL);
30823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
30833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            StatusUpdates.STATUS_ICON);
30843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        return mValues;
30853826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
30863826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
30873826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
30883826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mValues.clear();
30893826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
309072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            StatusUpdates.PRESENCE);
3091bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return mValues;
3092d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3093d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3094619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3095627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            String[] selectionArgs, boolean callerIsSyncAdapter) {
3096627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3097dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mGroupIdCache.clear();
3098743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
3099743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        ContentValues updatedValues;
3100743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
3101627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            updatedValues = mValues;
3102627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            updatedValues.clear();
3103dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            updatedValues.putAll(values);
3104627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
3105627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } else {
3106627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            updatedValues = values;
3107627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
3108627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3109627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
3110627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
3111627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            mVisibleTouched = true;
31124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
31134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
31144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
311515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            final long groupId = ContentUris.parseId(uri);
311615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(groupId);
311715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3118d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    Groups.ACCOUNT_TYPE}, Groups._ID + "=?", mSelectionArgs1, null,
3119385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                    null, null);
31203716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            String accountName;
31213716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            String accountType;
3122385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            try {
31233716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                while (c.moveToNext()) {
31243716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    accountName = c.getString(0);
31253716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    accountType = c.getString(1);
3126d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
31273716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        Account account = new Account(accountName, accountType);
31283716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
31293716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                                new Bundle());
3130d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                        break;
3131d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    }
3132d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
3133d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            } finally {
3134a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov                c.close();
3135a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            }
3136d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3137d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        return count;
3138d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3139d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3140d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3141d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            String[] selectionArgs) {
3142d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
3143d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
3144d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            mVisibleTouched = true;
3145d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3146d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        return count;
3147d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
31482e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
31492e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs) {
31502e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
31512e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
31522e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
31532e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
3154d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
315509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        int count = 0;
315609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
315709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                new String[] { RawContacts._ID }, selection,
315809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                selectionArgs, null, null, null);
315909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        try {
3160332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov            while (cursor.moveToNext()) {
3161d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                long rawContactId = cursor.getLong(0);
31626ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                updateRawContact(rawContactId, values);
31636ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                count++;
31646ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
31656ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } finally {
31666ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            cursor.close();
3167547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
3168547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
3169547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        return count;
3170547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro    }
3171547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
3172547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro    private int updateRawContact(long rawContactId, ContentValues values) {
31733716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        final String selection = RawContacts._ID + " = " + rawContactId;
31743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
3175547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro                && values.getAsInteger(RawContacts.DELETED) == 0);
3176547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        int previousDeleted = 0;
3177547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        String accountType = null;
3178547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        String accountName = null;
3179547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (requestUndoDelete) {
3180547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
3181547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro                    null, null, null, null);
31823716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            try {
31833716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (cursor.moveToFirst()) {
31843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
31853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
31863716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
31873716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
31883716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            } finally {
31893716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                cursor.close();
31903716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            }
31913716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
31923716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
31933716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
31943716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, null);
31953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (count != 0) {
31963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            if (values.containsKey(RawContacts.STARRED)) {
31973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                mContactAggregator.updateStarred(rawContactId);
31983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            }
31993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            if (values.containsKey(RawContacts.SOURCE_ID)) {
3200547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro                mContactAggregator.updateLookupKey(mDb, rawContactId);
3201547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            }
32026ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            if (requestUndoDelete && previousDeleted == 1) {
32036ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                // undo delete, needs aggregation again.
32046ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                mInsertedRawContacts.put(rawContactId, new Account(accountName, accountType));
32056ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
32066ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
32076ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return count;
32086ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
32096ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
32106ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private int updateData(Uri uri, ContentValues values, String selection,
32116ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            String[] selectionArgs, boolean callerIsSyncAdapter) {
32126ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        mValues.clear();
32136ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        mValues.putAll(values);
32146ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        mValues.remove(Data._ID);
32156ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
32166ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        mValues.remove(Data.MIMETYPE);
32176ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
32186ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
32196ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (packageName != null) {
32206ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
32216ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
32226ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
32236ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
32246ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
3225332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
32266ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
3227d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
3228d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        // content provider.
3229d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
3230d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            containsIsSuperPrimary = false;
3231d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
3232d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3233d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
3234d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            containsIsPrimary = false;
3235d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
3236d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3237d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3238d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        int count = 0;
3239d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3240d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3241d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
3242d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3243d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        try {
3244d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            while(c.moveToNext()) {
3245d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                count += updateData(mValues, c, callerIsSyncAdapter);
3246d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
32474458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        } finally {
32484458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            c.close();
32494458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
325049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov
325149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return count;
32524458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    }
32534458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov
32544458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
32554458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        if (values.size() == 0) {
32564458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return 0;
32574458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
32584458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov
32594458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
32604458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
32614458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        rowHandler.update(mDb, values, c, callerIsSyncAdapter);
32624458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
32634458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
32644458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            triggerAggregation(rawContactId);
3265d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
32664458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov
3267d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        return 1;
3268d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
32694458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov
32704458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
3271d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            String[] selectionArgs) {
3272d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        int count = 0;
327372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
32744458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
32754458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                selectionArgs, null, null, null);
32764458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        try {
327772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            while (cursor.moveToNext()) {
327872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                long contactId = cursor.getLong(0);
3279d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                updateContactOptions(contactId, values);
3280385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                count++;
3281bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            }
3282bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        } finally {
3283bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            cursor.close();
32840b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov        }
3285b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov
328635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        return count;
3287d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    }
32881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3289c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private int updateContactOptions(long contactId, ContentValues values) {
3290c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3291619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        mValues.clear();
3292619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3293a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                values, Contacts.CUSTOM_RINGTONE);
32944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
329535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                values, Contacts.SEND_TO_VOICEMAIL);
3296b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
329735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                values, Contacts.LAST_TIME_CONTACTED);
329835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3299d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3300763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3301385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                values, Contacts.STARRED);
3302619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3303619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Nothing to update - just return
3304619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (mValues.size() == 0) {
3305d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            return 0;
33064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
3307763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
33084da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
33094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            // Mark dirty when changing starred to trigger sync
33106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
33116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        }
33126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
33135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
33145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?", mSelectionArgs1);
33155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
33165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
33175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // being explicitly updated by clients.
3318fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov        mValues.clear();
3319fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
33205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3321a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
33225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
33235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
33245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
33255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3326763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                values, Contacts.TIMES_CONTACTED);
3327a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3328a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                values, Contacts.STARRED);
3329a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3330a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
3331a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
33325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
33335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    public void updateContactLastContactedTime(long contactId, long lastTimeContacted) {
33345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        mContactsLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
33355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        mContactsLastTimeContactedUpdate.bindLong(2, contactId);
3336763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        mContactsLastTimeContactedUpdate.execute();
33374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov    }
33384da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov
33394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
33405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
33415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
33425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
33432149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
33442149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        long rawContactId1, rawContactId2;
33452149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        if (rcId1 < rcId2) {
33462149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            rawContactId1 = rcId1;
33472149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            rawContactId2 = rcId2;
33482149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        } else {
33492149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            rawContactId2 = rcId1;
33502149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            rawContactId1 = rcId2;
33512149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        }
33522149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
33532149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
33542149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
33552149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
3356a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
3357a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
3358a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
3359a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
3360a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
33612149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
33622149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
33632149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
33642149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
33652149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    exceptionValues);
33662149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        }
33672149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
33682149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
33692149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1);
33702149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2);
33712149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
33722149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        long contactId1 = mDbHelper.getContactId(rawContactId1);
33732149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
3374f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
3375f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        long contactId2 = mDbHelper.getContactId(rawContactId2);
337642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
3377763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
3378f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // The return value is fake - we just confirm that we made a change, not count actual
33794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        // rows changed.
33804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        return 1;
33814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov    }
3382f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
3383f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    public void onAccountsUpdated(Account[] accounts) {
3384f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        mDb = mDbHelper.getWritableDatabase();
338542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        if (mDb == null) return;
338642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
338742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        HashSet<Account> existingAccounts = new HashSet<Account>();
338842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        boolean hasUnassignedContacts[] = new boolean[]{false};
338942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        mDb.beginTransaction();
339042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        try {
339142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            findValidAccounts(existingAccounts, hasUnassignedContacts,
339242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    Tables.RAW_CONTACTS, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE);
339342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            findValidAccounts(existingAccounts, hasUnassignedContacts,
339442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    Tables.GROUPS, Groups.ACCOUNT_NAME, Groups.ACCOUNT_TYPE);
3395ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3396916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    Tables.SETTINGS, Settings.ACCOUNT_NAME, Settings.ACCOUNT_TYPE);
3397ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3398916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
3399ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            // in the existingAccounts set will be extra accounts whose data must be deleted.
34007ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
34017ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            for (Account account : accounts) {
3402ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                accountsToDelete.remove(account);
3403ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3404ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3405ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            for (Account account : accountsToDelete) {
3406ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Log.d(TAG, "removing data for removed account " + account);
34074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String[] params = new String[] {account.name, account.type};
3408ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                mDb.execSQL(
3409d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        "DELETE FROM " + Tables.GROUPS +
34104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
34114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
3412e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                mDb.execSQL(
34135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        "DELETE FROM " + Tables.PRESENCE +
34144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
34154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                "SELECT " + RawContacts._ID +
34164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
3417763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                                " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3418ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                                " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
34195e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                mDb.execSQL(
34205e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                        "DELETE FROM " + Tables.RAW_CONTACTS +
34215e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                        " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3422dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                        " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
3423dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                mDb.execSQL(
3424dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                        "DELETE FROM " + Tables.SETTINGS +
3425dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                        " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
34265e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                        " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
34275e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            }
34284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
34294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            if (hasUnassignedContacts[0]) {
34304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3431d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                Account primaryAccount = null;
34325e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                for (Account account : accounts) {
34335e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    if (isWritableAccount(account)) {
34344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        primaryAccount = account;
3435d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        break;
3436d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    }
3437d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3438763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
34394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (primaryAccount != null) {
34404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String[] params = new String[] {primaryAccount.name, primaryAccount.type};
3441d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
34425e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    mDb.execSQL(
34435e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                            "UPDATE " + Tables.RAW_CONTACTS +
3444d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                            " SET " + RawContacts.ACCOUNT_NAME + "=?,"
3445d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                                    + RawContacts.ACCOUNT_TYPE + "=?" +
34464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " IS NULL" +
3447d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL", params);
3448d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3449d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    // We don't currently support groups for unsynced accounts, so this is for
3450d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    // the future
34514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    mDb.execSQL(
34524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                            "UPDATE " + Tables.GROUPS +
3453d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            " SET " + Groups.ACCOUNT_NAME + "=?,"
3454d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                                    + Groups.ACCOUNT_TYPE + "=?" +
3455d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            " WHERE " + Groups.ACCOUNT_NAME + " IS NULL" +
3456d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            " AND " + Groups.ACCOUNT_TYPE + " IS NULL", params);
3457d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3458d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3459ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
3460763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
3461b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            mDb.setTransactionSuccessful();
346271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov        } finally {
34634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            mDb.endTransaction();
3464b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
3465b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
3466b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
3467b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    /**
3468a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Finds all distinct accounts present in the specified table.
34694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
347082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void findValidAccounts(Set<Account> validAccounts, boolean[] hasUnassignedContacts,
34714da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            String table, String accountNameColumn, String accountTypeColumn) {
34724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        Cursor c = mDb.rawQuery("SELECT DISTINCT " + accountNameColumn + "," + accountTypeColumn
34736bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                + " FROM " + table, null);
34746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        try {
347500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            while (c.moveToNext()) {
3476a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (c.isNull(0) && c.isNull(1)) {
34773653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                    hasUnassignedContacts[0] = true;
347882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                } else {
34794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
34804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                }
34813653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
34823653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        } finally {
34833653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            c.close();
34843653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        }
3485a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
3486a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3487a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
3488a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
3489a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
3490a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
3491a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        for (String key : keys) {
3492a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
3493a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                return false;
3494a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3495a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
3496a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return true;
3497a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
3498a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3499a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
3500a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
3501a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
3502a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
3503a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        for (String key : keys) {
3504a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            if (values.containsKey(key)) {
3505a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                return true;
3506a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3507a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
3508a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return false;
3509a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
3510a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3511a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    @Override
3512a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
3513a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder) {
3514a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (VERBOSE_LOGGING) {
3515a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            Log.v(TAG, "query: " + uri);
3516a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
3517a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3518a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
3519a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3520a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
3521a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String groupBy = null;
3522a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String limit = getLimit(uri);
3523a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
35244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        // TODO: Consider writing a test case for RestrictionExceptions when you
352582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        // write a new query() block to make sure it protects restricted data.
352689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        final int match = sUriMatcher.match(uri);
35272815f58f72f109790585931f601a63ddc02536a5Evan Millar        switch (match) {
35282815f58f72f109790585931f601a63ddc02536a5Evan Millar            case SYNCSTATE:
35292815f58f72f109790585931f601a63ddc02536a5Evan Millar                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
353048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        sortOrder);
353182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
35324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            case CONTACTS: {
353348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, uri, projection);
35344da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                break;
353548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
353648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
353748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case CONTACTS_ID: {
3538ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                long contactId = ContentUris.parseId(uri);
353982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, uri, projection);
354089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
3541ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Contacts._ID + "=?");
35424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
35434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
3544a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
35455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case CONTACTS_LOOKUP:
354645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
35475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
35485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                int segmentCount = pathSegments.size();
35495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                if (segmentCount < 3) {
3550155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
3551155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                }
3552155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
3553155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                if (segmentCount == 4) {
3554155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
3555155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3556155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
35572352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                    String[] args;
3558155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                    if (selectionArgs == null) {
35595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        args = new String[2];
356045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    } else {
35615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        args = new String[selectionArgs.length + 2];
35625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
3563892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
3564892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    args[0] = String.valueOf(contactId);
35655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    args[1] = lookupKey;
35665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
35675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
35685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            groupBy, limit);
3569892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getCount() != 0) {
3570892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        return c;
3571892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
3572892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov
3573892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    c.close();
357445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                }
357545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
357645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, uri, projection);
357745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
357845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
357945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
358045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                break;
35815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
35825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3583a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            case CONTACTS_AS_VCARD: {
3584ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                // When reading as vCard always use restricted view
35855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                final String lookupKey = uri.getPathSegments().get(2);
3586a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
3587a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                qb.setProjectionMap(sContactsVCardProjectionMap);
3588a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
3589ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
3590ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Contacts._ID + "=?");
3591ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
35924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
359382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
359489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            case CONTACTS_FILTER: {
35954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, uri, projection);
35964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
35974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
359848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
359982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
36004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
36014da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    qb.appendWhere(sb.toString());
36024da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                }
360348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
360448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
360548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
36065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
360782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case CONTACTS_STREQUENT: {
360889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                String filterSql = null;
36094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
361008768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                        && uri.getPathSegments().size() > 3) {
361108768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
361208768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
361308768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
36144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
3615ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    filterSql = sb.toString();
3616ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
3617ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
36185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, uri, projection);
361982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
362007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String[] starredProjection = null;
36217d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                String[] frequentProjection = null;
36227d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                if (projection != null) {
36237d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    starredProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
36247d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    frequentProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
36257d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                }
36267d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
362707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                // Build the first query for starred
362807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterSql != null) {
362907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(filterSql);
363007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
363107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                qb.setProjectionMap(sStrequentStarredProjectionMap);
363207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
36335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
363407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
363507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                // Build the second query for frequent
363607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                qb = new SQLiteQueryBuilder();
363707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, uri, projection);
363807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterSql != null) {
363907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(filterSql);
364007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
364107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                qb.setProjectionMap(sStrequentFrequentProjectionMap);
364207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                final String frequentQuery = qb.buildQuery(frequentProjection,
36432a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
36442a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
36452a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
364607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
364720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                // Put them together
3648155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3649155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
3650155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
3651155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                if (c != null) {
3652155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                    c.setNotificationUri(getContext().getContentResolver(),
3653155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                            ContactsContract.AUTHORITY_URI);
3654155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                }
3655155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                return c;
3656155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov            }
3657155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov
3658155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov            case CONTACTS_GROUP: {
3659155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, uri, projection);
36602352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
3661155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
36625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
36635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
3664a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                break;
36655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
36665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3667a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov            case CONTACTS_DATA: {
36687d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                long contactId = Long.parseLong(uri.getPathSegments().get(1));
36697d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, false);
36707d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
36717d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
36727d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                break;
36737d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa            }
36747d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
36757d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa            case CONTACTS_PHOTO: {
36767d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                long contactId = Long.parseLong(uri.getPathSegments().get(1));
36777d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, false);
36787d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
36797d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
36807d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
36817d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                break;
36827d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa            }
36837d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
3684a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov            case PHONES: {
36855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
36875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
3688ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
368982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
369089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            case PHONES_ID: {
369189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3692ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3693ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3694ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(" AND " + Data._ID + "=?");
369548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
369682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            }
36974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov
369848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_FILTER: {
369948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
37004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
370148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
370248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
370348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
37045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    sb.append(" AND (");
3705763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
37064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                    boolean orNeeded = false;
37074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                    String normalizedName = NameNormalizer.normalize(filterParam);
37084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                    if (normalizedName.length() > 0) {
37095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
37105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, null, false);
3711763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                        orNeeded = true;
37124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    }
37134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov
37144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                    if (isPhoneNumber(filterParam)) {
37154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                        if (orNeeded) {
37164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                            sb.append(" OR ");
37175ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                        }
37185ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
371982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
37204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        sb.append(Data._ID +
37214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
3722e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                                  + " FROM " + Tables.PHONE_LOOKUP
3723e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
3724e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                        sb.append(reversed);
3725e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                        sb.append("')");
372682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    }
3727e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                    sb.append(")");
3728e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                    qb.appendWhere(sb);
3729e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                }
37304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
373182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (sortOrder == null) {
37324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
37334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                }
3734a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
3735a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
3736a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
3737a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case EMAILS: {
37384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3739a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
3740a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
3741a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
3742892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov
3743a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case EMAILS_ID: {
3744a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                setTablesAndProjectionMapForData(qb, uri, projection, false);
3745e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3746e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
3747e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        + " AND " + Data._ID + "=?");
3748892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                break;
3749892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            }
3750892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov
3751e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov            case EMAILS_LOOKUP: {
3752e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3753e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
3754e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
3755a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3756a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    qb.appendWhere(" AND " + Email.DATA + "=?");
3757a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
3758ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3759b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            }
3760ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
376189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            case EMAILS_FILTER: {
3762ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                setTablesAndProjectionMapForData(qb, uri, projection, true);
3763ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
3764ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (uri.getPathSegments().size() > 2) {
3765ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    String filterParam = uri.getLastPathSegment();
3766b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3767ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    sb.append(" AND (");
37684da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov
37694da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    if (!filterParam.contains("@")) {
3770ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        String normalizedName = NameNormalizer.normalize(filterParam);
3771ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        if (normalizedName.length() > 0) {
3772ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                            sb.append(Data.RAW_CONTACT_ID + " IN ");
3773ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, null, false);
3774b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                            sb.append(" OR ");
3775ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        }
377689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                    }
377789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov
3778ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    sb.append(Email.DATA + " LIKE ");
3779ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    sb.append(DatabaseUtils.sqlEscapeString(filterParam + '%'));
3780ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    sb.append(")");
3781b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                    qb.appendWhere(sb);
37820c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                }
3783b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
3784b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                if (sortOrder == null) {
3785b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3786b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                }
378731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                break;
3788d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            }
37892d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov
37902d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov            case POSTALS: {
37912d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37922d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
379331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
3794d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                break;
3795d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov            }
379631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
379731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case POSTALS_ID: {
379831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
379931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
38005b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
38015b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
38025b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
38035b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                break;
38045b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov            }
38055b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
38065b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov            case RAW_CONTACTS: {
38075b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                setTablesAndProjectionMapForRawContacts(qb, uri);
380876dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                break;
38095b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov            }
38105b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
38115b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov            case RAW_CONTACTS_ID: {
38125b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
38135b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                setTablesAndProjectionMapForRawContacts(qb, uri);
38145b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
38155b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
3816763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                break;
38177581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov            }
38187581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
38195b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
382031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
382131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3822eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
3823eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
3824eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
382589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            }
3826e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3827e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            case DATA: {
3828e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                setTablesAndProjectionMapForData(qb, uri, projection, false);
3829b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                break;
3830e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            }
383182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
3832b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            case DATA_ID: {
3833e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                setTablesAndProjectionMapForData(qb, uri, projection, false);
3834e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
383582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
3836b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                break;
3837e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            }
3838e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3839e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            case PHONE_LOOKUP: {
3840eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
3841eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                if (TextUtils.isEmpty(sortOrder)) {
3842eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                    // Default the sort order to something reasonable so we get consistent
384382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    // results when callers don't request an ordering
38440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
38455ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                }
38465ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
38475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
384882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, number);
38490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
38504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov
38514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                // Phone lookup cannot be combined with a selection
38525ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                selection = null;
38535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                selectionArgs = null;
38545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
3855c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3856174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
3857174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            case GROUPS: {
3858c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3859c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                qb.setProjectionMap(sGroupsProjectionMap);
3860c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                appendAccountFromParameter(qb, uri);
38612d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                break;
3862174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            }
3863174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
3864174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            case GROUPS_ID: {
3865174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3866c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                qb.setProjectionMap(sGroupsProjectionMap);
3867c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
38681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
3869b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                break;
38701b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            }
38711b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
38721b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case GROUPS_SUMMARY: {
38731b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
3874b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setProjectionMap(sGroupsSummaryProjectionMap);
38751b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                appendAccountFromParameter(qb, uri);
38761b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                groupBy = Groups._ID;
38771b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
38781b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            }
38791b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
3880b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
38811b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
38821b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
38831b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
38841b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            }
38851b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
3886b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
38871b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
388871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                String filter = null;
38891b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
38901b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
38911b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                }
389246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                final int maxSuggestions;
3893a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (limit != null) {
389446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    maxSuggestions = Integer.parseInt(limit);
389546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                } else {
389646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
389746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                }
389846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
3899a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, uri, projection);
39004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov
39014da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
390246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                        maxSuggestions, filter);
390346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
390446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
390509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case SETTINGS: {
390609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                qb.setTables(Tables.SETTINGS);
390709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                qb.setProjectionMap(sSettingsProjectionMap);
390809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                appendAccountFromParameter(qb, uri);
3909d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3910d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                // When requesting specific columns, this query requires
3911d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                // late-binding of the GroupMembership MIME-type.
3912d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
3913d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
3914d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                if (projection != null && projection.length != 0 &&
3915d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
3916385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3917d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
3918d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                if (projection != null && projection.length != 0 &&
3919385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
3920d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3921d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
3922d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3923d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
39247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
39257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
39267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case STATUS_UPDATES: {
39277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
39284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
3929f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            }
3930c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
39314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case STATUS_UPDATES_ID: {
39324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                setTableAndProjectionMapForStatusUpdates(qb, projection);
39337f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
39347f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
3935ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                break;
3936ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
3937ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3938ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            case SEARCH_SUGGESTIONS: {
3939ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
3940ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
39415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case SEARCH_SHORTCUT: {
39435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                long contactId = ContentUris.parseId(uri);
39445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, contactId, projection);
39455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
3946038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana
3947038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            case LIVE_FOLDERS_CONTACTS:
3948038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                qb.setTables(mDbHelper.getContactView());
3949038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                qb.setProjectionMap(sLiveFoldersProjectionMap);
39505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
39515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
39534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setTables(mDbHelper.getContactView());
39544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sLiveFoldersProjectionMap);
39554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
39564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
39574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
395809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
395909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
396009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
396109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
396209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                break;
396309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
396409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
396509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
396609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
396709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
396809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
396909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                break;
397009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
397109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case RAW_CONTACT_ENTITIES: {
397209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
397309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                break;
3974a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3975a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3976a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case RAW_CONTACT_ENTITY_ID: {
3977a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
3978a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
3979a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
3980a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
3981a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
3982a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3983a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3984a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            default:
3985a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
3986a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        sortOrder, limit);
3987a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
3988a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3989a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
3990a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
3991a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3992a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
3993a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
3994a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String limit) {
3995a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (projection != null && projection.length == 1
3996a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                && BaseColumns._COUNT.equals(projection[0])) {
3997a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            qb.setProjectionMap(sCountProjectionMap);
3998a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
3999a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
4000a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                sortOrder, limit);
4001a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c != null) {
4002a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
400309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
4004bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        return c;
4005bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    }
4006bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4007bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
4008ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
4009bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
4010bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4011ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        long contactId = lookupContactIdBySourceIds(db, segments);
4012ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (contactId == -1) {
4013bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
4014bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        }
4015bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4016bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        return contactId;
4017de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    }
4018ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4019ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private interface LookupBySourceIdQuery {
4020ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
4021ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4022ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String COLUMNS[] = {
4023ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                RawContacts.CONTACT_ID,
4024ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
4025ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
4026ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                RawContacts.SOURCE_ID
4027ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
4028ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4029ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        int CONTACT_ID = 0;
4030ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
4031ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        int ACCOUNT_NAME = 2;
4032ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        int SOURCE_ID = 3;
4033ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4034ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4035ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
4036ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
4037ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        int sourceIdCount = 0;
4038ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
4039ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
4040ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (segment.sourceIdLookup) {
4041ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sourceIdCount++;
4042ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4043ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4044bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4045ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sourceIdCount == 0) {
4046bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            return -1;
4047bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        }
4048bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4049bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        // First try sync ids
4050bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4051bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
4052bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
4053bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
4054bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (segment.sourceIdLookup) {
4055bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
4056ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sb.append(",");
4057bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
4058bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        }
4059ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
4060ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
4061ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4062ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
4063f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                 sb.toString(), null, null, null, null);
4064ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
4065ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            while (c.moveToNext()) {
4066ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
4067ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
4068f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                int accountHashCode =
4069ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
4070ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
4071bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
4072bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
4073bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    if (segment.sourceIdLookup && accountHashCode == segment.accountHashCode
4074bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                            && segment.key.equals(sourceId)) {
4075bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
4076bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        break;
4077ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                    }
4078f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                }
4079bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
4080bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        } finally {
4081bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            c.close();
4082bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        }
4083bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4084bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        return getMostReferencedContactId(segments);
4085bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    }
4086bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4087bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private interface LookupByDisplayNameQuery {
4088bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
4089bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4090bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String COLUMNS[] = {
4091bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                RawContacts.CONTACT_ID,
4092bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
4093bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
4094bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
4095bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        };
4096bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4097bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        int CONTACT_ID = 0;
4098ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
4099ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        int ACCOUNT_NAME = 2;
4100e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        int NORMALIZED_NAME = 3;
4101ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4102f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov
4103ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
4104ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
4105ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        int displayNameCount = 0;
41062d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        for (int i = 0; i < segments.size(); i++) {
410792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
410892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (!segment.sourceIdLookup) {
410992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                displayNameCount++;
411092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
41112d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        }
41122d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill
41135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (displayNameCount == 0) {
41145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
41155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
411692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
411792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        // First try sync ids
411892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
411992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
412092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
412192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
412292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (!segment.sourceIdLookup) {
412392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
412492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
412592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
412692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
412792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
412892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
412992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
413092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
413192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
413292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
413392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
413492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
41355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
41365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
41375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
41385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
41395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
41405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
41415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
41425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (!segment.sourceIdLookup && accountHashCode == segment.accountHashCode
41435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
41445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
41455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
41465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
41475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
41485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
41505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
41515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
41545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
41575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
41585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
41595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
41605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
41615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
416392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int bestRefCount = 0;
41645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
41665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
41675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
41695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
41705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
41715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
41725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
41735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
41745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
41755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
41765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
41775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
41785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
41795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
41805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
41815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
418292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
418392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
41845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
41855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
41865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
41875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
41885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
41925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String[] projection) {
41935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
41945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        boolean excludeRestrictedData = false;
41955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
41965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
419792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (requestingPackage != null) {
419892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
41995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(mDbHelper.getContactView(excludeRestrictedData));
42015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
42025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Contacts.CONTACT_PRESENCE)) {
42035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
420492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    " ON (" + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ")");
42055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
42075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Contacts.CONTACT_STATUS,
42085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
42095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
421092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
42115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
42125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
421392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
421492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
421592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
421692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
42175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        qb.setTables(sb.toString());
42185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
421992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
422092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
422192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
42225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
42235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        boolean excludeRestrictedData = false;
422492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
422592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
42265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (requestingPackage != null) {
422792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
422892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
422992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
423092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        qb.setTables(sb.toString());
423192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        qb.setProjectionMap(sRawContactsProjectionMap);
423292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
423392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
423492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
423592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private void setTablesAndProjectionMapForRawContactsEntities(SQLiteQueryBuilder qb, Uri uri) {
423692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        // Note: currently, "export only" equals to "restricted", but may not in the future.
423792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
423892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                Data.FOR_EXPORT_ONLY, false);
423992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
424092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
424192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
424292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (requestingPackage != null) {
424392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            excludeRestrictedData = excludeRestrictedData
424492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
424592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
424692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        qb.setTables(mDbHelper.getContactEntitiesView(excludeRestrictedData));
424792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        qb.setProjectionMap(sRawContactsEntityProjectionMap);
42485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
42495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
425092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
425192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
425292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            String[] projection, boolean distinct) {
425392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
425492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        // Note: currently, "export only" equals to "restricted", but may not in the future.
425592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
425692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                Data.FOR_EXPORT_ONLY, false);
425792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
425892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
425992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
426092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (requestingPackage != null) {
426192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            excludeRestrictedData = excludeRestrictedData
426292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
426392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
426492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
426592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(mDbHelper.getDataView(excludeRestrictedData));
426692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(" data");
426792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
426892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        // Include aggregated presence when requested
426992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.CONTACT_PRESENCE)) {
427092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
42715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    " ON (" + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + "="
42725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    + RawContacts.CONTACT_ID + ")");
42735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
427592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        // Include aggregated status updates when requested
427692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
42775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Data.CONTACT_STATUS,
42785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Data.CONTACT_STATUS_RES_PACKAGE,
42795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Data.CONTACT_STATUS_ICON,
42805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Data.CONTACT_STATUS_LABEL,
42815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Data.CONTACT_STATUS_TIMESTAMP)) {
42825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
42835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
42845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
42855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
42865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // Include individual presence when requested
42895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE)) {
42905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
42915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "="
42925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    + DataColumns.CONCRETE_ID + ")");
42935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // Include individual status updates when requested
429692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
429792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                Data.STATUS,
429892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                Data.STATUS_RES_PACKAGE,
42995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Data.STATUS_ICON,
43005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Data.STATUS_LABEL,
43015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Data.STATUS_TIMESTAMP)) {
43025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
43035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
43045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            + DataColumns.CONCRETE_ID + ")");
43055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
43065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
43075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        qb.setTables(sb.toString());
43085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);
43095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
43105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
43115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
431292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
431392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            String[] projection) {
431492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
431592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(mDbHelper.getDataView());
431692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(" data");
431792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
431892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, StatusUpdates.PRESENCE)) {
431992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
432092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    " ON(" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID
432192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
432292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
4323ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
4324ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
4325ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov                StatusUpdates.STATUS,
4326ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
43275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                StatusUpdates.STATUS_ICON,
43285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
43295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
43305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
43315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    " ON(" + Tables.STATUS_UPDATES + "." + StatusUpdatesColumns.DATA_ID
43325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
43335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
43345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        qb.setTables(sb.toString());
43355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
43365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
43375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
43385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
43395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
43405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
43415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (!TextUtils.isEmpty(accountName)) {
43425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
43435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
43445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
43455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
43465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
43475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            qb.appendWhere("1");
43485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
43495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
43505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
43515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private String appendAccountToSelection(Uri uri, String selection) {
43525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
43535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
43545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (!TextUtils.isEmpty(accountName)) {
43555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
43565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
43575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
43585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
43595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!TextUtils.isEmpty(selection)) {
43605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                selectionSb.append(" AND (");
43615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                selectionSb.append(selection);
4362763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                selectionSb.append(')');
4363763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            }
436482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            return selectionSb.toString();
43657ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        } else {
43667ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            return selection;
43677ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        }
4368916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
4369916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4370916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
4371916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
4372916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     *
4373916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
4374916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
4375916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
4376916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private String getLimit(Uri uri) {
43777ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        String limitParam = getQueryParameter(uri, "limit");
43787ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        if (limitParam == null) {
43797ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            return null;
43807ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        }
4381916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // make sure that the limit is a non-negative integer
438203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        try {
438303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            int l = Integer.parseInt(limitParam);
438403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (l < 0) {
438503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
438630cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov                return null;
438730cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            }
43885e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            return String.valueOf(l);
43895e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } catch (NumberFormatException ex) {
43905e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
43917ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            return null;
43927ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        }
439303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
439403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
439503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    /**
4396916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
439703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
439803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov     */
4399916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
4400174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        int len = cons.length();
440103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
440203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        for (int i = 0; i < len; i++) {
440303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            char c = cons.charAt(i);
440403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
440503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
440603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                continue;
440703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
44085e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
44095e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    || (c == '#') || (c == '*')) {
44105e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                continue;
44115e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            }
44125e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
44135e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                continue;
44145e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            }
44155e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
44165e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                continue;
4417174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            }
4418174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
4419174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            return false;
4420174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
4421174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
4422174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        return true;
4423174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
4424174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
4425174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    String getContactsRestrictions() {
4426174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (mDbHelper.hasAccessToRestrictedData()) {
4427174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            return "1";
4428174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
4429174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            return RawContacts.IS_RESTRICTED + "=0";
4430174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
4431174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
4432174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
44333716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
44343716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (mDbHelper.hasAccessToRestrictedData()) {
44353716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return "1";
44363716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        } else {
4437174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
4438174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
4439174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
4440174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
4441174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
444204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann    @Override
444304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
444404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann        int match = sUriMatcher.match(uri);
444504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann        switch (match) {
444604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            case CONTACTS_PHOTO: {
4447174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                if (!"r".equals(mode)) {
4448174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                    throw new FileNotFoundException("Mode " + mode + " not supported.");
4449174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                }
4450174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
44515e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                String sql =
44525e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                        "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
44533d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                        " WHERE " + Data._ID + "=" + Contacts.PHOTO_ID
44545e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                                + " AND " + RawContacts.CONTACT_ID + "=?";
445504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                SQLiteDatabase db = mDbHelper.getReadableDatabase();
445604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql,
445704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        new String[]{uri.getPathSegments().get(1)});
445804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
445904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann
446004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            case CONTACTS_AS_VCARD: {
44613d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                final String lookupKey = uri.getPathSegments().get(2);
44623d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
44633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                final String selection = Contacts._ID + "=" + contactId;
44643716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
44653716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // When opening a contact as file, we pass back contents as a
44663716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // vCard-encoded stream. We build into a local buffer first,
44673716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // then pipe into MemoryFile once the exact size is known.
44683716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
44693716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                outputRawContactsAsVCard(localStream, selection, null);
44703d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                return buildAssetFileDescriptor(localStream);
44713d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            }
44723d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov
44733d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            default:
447404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                throw new FileNotFoundException("No file at: " + uri);
447504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann        }
447604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann    }
447704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann
447804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
447904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann    private static final String VCARD_TYPE_DEFAULT = "default";
448004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann
448104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann    /**
448204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
448304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann     * contents of the given {@link ByteArrayOutputStream}.
448404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann     */
448504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
448604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann        AssetFileDescriptor fd = null;
448704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann        try {
448804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            stream.flush();
448904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann
44905e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            final byte[] byteData = stream.toByteArray();
44915e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            final int size = byteData.length;
44923716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
44933716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
44943716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            memoryFile.writeBytes(byteData, 0, 0, size);
44953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            memoryFile.deactivate();
44963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
44973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
44983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        } catch (IOException e) {
44995e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
450003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
450104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann        return fd;
450204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann    }
45033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
45043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /**
45053716f1447ceb21180d1301790eabd8b9453f486dDave Santoro     * Output {@link RawContacts} matching the requested selection in the vCard
45063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro     * format to the given {@link OutputStream}. This method returns silently if
45073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro     * any errors encountered.
45083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro     */
45093716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    private void outputRawContactsAsVCard(OutputStream stream, String selection,
45103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            String[] selectionArgs) {
45113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        final Context context = this.getContext();
45123716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        final VCardComposer composer = new VCardComposer(context, VCARD_TYPE_DEFAULT, false);
45133716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        composer.addHandler(composer.new HandlerForOutputStream(stream));
45143716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
45153716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // No extra checks since composer always uses restricted views
45163716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (!composer.init(selection, selectionArgs))
45173716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return;
45183716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
45193716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        while (!composer.isAfterLast()) {
45203716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            if (!composer.createOneEntry()) {
452104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                Log.w(TAG, "Failed to output a contact.");
452204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
452304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann        }
452403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        composer.terminate();
45255e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
45265e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
452703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    /**
45285e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov     * An implementation of EntityIterator that joins the contacts and data tables
45295e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov     * and consumes all the data rows for a contact in order to build the Entity for a contact.
45305e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov     */
45315e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private static class RawContactsEntityIterator implements EntityIterator {
45322352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        private final Cursor mEntityCursor;
45333d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        private volatile boolean mIsClosed;
45342352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
453504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann        private static final String[] DATA_KEYS = new String[]{
45362352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                Data.DATA1,
453703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                Data.DATA2,
45382352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                Data.DATA3,
45399c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov                Data.DATA4,
454003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                Data.DATA5,
4541a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov                Data.DATA6,
4542a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov                Data.DATA7,
45432352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                Data.DATA8,
45442352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                Data.DATA9,
45452352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                Data.DATA10,
45462352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                Data.DATA11,
45472352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                Data.DATA12,
45485e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                Data.DATA13,
45495e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                Data.DATA14,
45505e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                Data.DATA15,
45515e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                Data.SYNC1,
45525e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                Data.SYNC2,
45535e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                Data.SYNC3,
45545e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                Data.SYNC4};
45555e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
45565e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        public static final String[] PROJECTION = new String[]{
45575e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
45585e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
45595e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                RawContacts.SOURCE_ID,
45605e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                RawContacts.VERSION,
45615e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                RawContacts.DIRTY,
45625e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                RawContacts.Entity.DATA_ID,
4563763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                Data.RES_PACKAGE,
4564763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                Data.MIMETYPE,
45657ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                Data.DATA1,
4566763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                Data.DATA2,
4567763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                Data.DATA3,
4568763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                Data.DATA4,
4569763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                Data.DATA5,
4570763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                Data.DATA6,
4571a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.DATA7,
4572a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.DATA8,
4573a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.DATA9,
457446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                Data.DATA10,
457546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                Data.DATA11,
457646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                Data.DATA12,
457782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.DATA13,
457882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.DATA14,
457982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.DATA15,
4580a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.SYNC1,
458182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.SYNC2,
458282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.SYNC3,
4583a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.SYNC4,
4584a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                RawContacts._ID,
4585a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.IS_PRIMARY,
4586a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.IS_SUPER_PRIMARY,
45873296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.DATA_VERSION,
458882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID,
4589f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                RawContacts.SYNC1,
4590f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                RawContacts.SYNC2,
4591f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                RawContacts.SYNC3,
4592f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                RawContacts.SYNC4,
4593f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                RawContacts.DELETED,
459482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                RawContacts.CONTACT_ID,
4595ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                RawContacts.STARRED,
4596ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                RawContacts.IS_RESTRICTED};
45970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
45980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        private static final int COLUMN_ACCOUNT_NAME = 0;
45990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        private static final int COLUMN_ACCOUNT_TYPE = 1;
4600b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        private static final int COLUMN_SOURCE_ID = 2;
46010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        private static final int COLUMN_VERSION = 3;
4602a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_DIRTY = 4;
4603a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_DATA_ID = 5;
46040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        private static final int COLUMN_RES_PACKAGE = 6;
4605a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_MIMETYPE = 7;
4606a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_DATA1 = 8;
4607a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_RAW_CONTACT_ID = 27;
4608a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_IS_PRIMARY = 28;
4609a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_IS_SUPER_PRIMARY = 29;
4610a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_DATA_VERSION = 30;
4611a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_GROUP_SOURCE_ID = 31;
4612a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_SYNC1 = 32;
4613a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_SYNC2 = 33;
4614a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_SYNC3 = 34;
4615a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_SYNC4 = 35;
4616a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_DELETED = 36;
4617a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_CONTACT_ID = 37;
4618a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_STARRED = 38;
4619a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        private static final int COLUMN_IS_RESTRICTED = 39;
4620a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4621a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        public RawContactsEntityIterator(ContactsProvider2 provider, Uri entityUri,
4622a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String contactsIdString,
4623a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String selection, String[] selectionArgs, String sortOrder) {
4624a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            mIsClosed = false;
4625a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            Uri uri;
4626a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            if (contactsIdString != null) {
4627a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, contactsIdString);
4628a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                uri = Uri.withAppendedPath(uri, RawContacts.Entity.CONTENT_DIRECTORY);
4629a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            } else {
4630a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                uri = ContactsContract.RawContactsEntity.CONTENT_URI;
4631a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4632a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            final Uri.Builder builder = uri.buildUpon();
4633a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String query = entityUri.getQuery();
4634a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            builder.encodedQuery(query);
4635a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            mEntityCursor = provider.query(builder.build(),
4636a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    PROJECTION, selection, selectionArgs, sortOrder);
46370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            mEntityCursor.moveToFirst();
4638a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
46390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4640a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        public void reset() throws RemoteException {
4641a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            if (mIsClosed) {
4642b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                throw new IllegalStateException("calling reset() when the iterator is closed");
46430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
46440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            mEntityCursor.moveToFirst();
46450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
46460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
46470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        public void close() {
46480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (mIsClosed) {
4649a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                throw new IllegalStateException("closing when already closed");
4650a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
46510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            mIsClosed = true;
4652a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            mEntityCursor.close();
4653a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4654a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4655a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        public boolean hasNext() throws RemoteException {
4656a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            if (mIsClosed) {
4657a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                throw new IllegalStateException("calling hasNext() when the iterator is closed");
4658a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4659a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4660a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return !mEntityCursor.isAfterLast();
4661a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4662a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4663a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        public Entity next() throws RemoteException {
4664a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            if (mIsClosed) {
4665a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                throw new IllegalStateException("calling next() when the iterator is closed");
4666a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4667a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            if (!hasNext()) {
4668a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                throw new IllegalStateException("you may only call next() if hasNext() is true");
4669a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4670a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4671a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
4672385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
4673385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            final long rawContactId = c.getLong(COLUMN_RAW_CONTACT_ID);
4674385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
4675385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            // we expect the cursor is already at the row we need to read from
4676385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            ContentValues contactValues = new ContentValues();
4677385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
4678385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
4679385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            contactValues.put(RawContacts._ID, rawContactId);
4680a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.DIRTY, c.getLong(COLUMN_DIRTY));
4681a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.VERSION, c.getLong(COLUMN_VERSION));
4682a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
4683a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.SYNC1, c.getString(COLUMN_SYNC1));
4684a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.SYNC2, c.getString(COLUMN_SYNC2));
4685a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.SYNC3, c.getString(COLUMN_SYNC3));
4686a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.SYNC4, c.getString(COLUMN_SYNC4));
4687a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.DELETED, c.getLong(COLUMN_DELETED));
4688a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.CONTACT_ID, c.getLong(COLUMN_CONTACT_ID));
4689a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.STARRED, c.getLong(COLUMN_STARRED));
4690a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            contactValues.put(RawContacts.IS_RESTRICTED, c.getInt(COLUMN_IS_RESTRICTED));
4691a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            Entity contact = new Entity(contactValues);
4692a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4693a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            // read data rows until the contact id changes
4694a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            do {
46950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                if (rawContactId != c.getLong(COLUMN_RAW_CONTACT_ID)) {
46960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    break;
46974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
4698f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov//                if (c.isNull(COLUMN_CONTACT_ID)) {
4699f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov//                    continue;
4700e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey//                }
4701e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                // add the data to to the contact
4702e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ContentValues dataValues = new ContentValues();
4703e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                dataValues.put(Data._ID, c.getLong(COLUMN_DATA_ID));
4704fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                dataValues.put(Data.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
4705fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                dataValues.put(Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
4706e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                dataValues.put(Data.IS_PRIMARY, c.getLong(COLUMN_IS_PRIMARY));
4707e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                dataValues.put(Data.IS_SUPER_PRIMARY, c.getLong(COLUMN_IS_SUPER_PRIMARY));
4708e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
4709e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
4710e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
4711e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                            c.getString(COLUMN_GROUP_SOURCE_ID));
47124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
47134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
47144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                for (int i = 0; i < DATA_KEYS.length; i++) {
47154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    final int columnIndex = i + COLUMN_DATA1;
47164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String key = DATA_KEYS[i];
47174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    if (c.isNull(columnIndex)) {
47184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        // don't put anything
47194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    } else if (c.isLong(columnIndex)) {
47204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        dataValues.put(key, c.getLong(columnIndex));
4721e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    } else if (c.isFloat(columnIndex)) {
4722f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        dataValues.put(key, c.getFloat(columnIndex));
4723f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                    } else if (c.isString(columnIndex)) {
4724e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        dataValues.put(key, c.getString(columnIndex));
4725e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    } else if (c.isBlob(columnIndex)) {
4726e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        dataValues.put(key, c.getBlob(columnIndex));
4727e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    }
4728fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                }
4729fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                contact.addSubValue(Data.CONTENT_URI, dataValues);
4730e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            } while (mEntityCursor.moveToNext());
4731e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
4732e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return contact;
4733e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
4734e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    }
4735e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
4736e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    /**
4737e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong     * An implementation of EntityIterator that joins the contacts and data tables
4738e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong     * and consumes all the data rows for a contact in order to build the Entity for a contact.
4739e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong     */
4740e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private static class GroupsEntityIterator implements EntityIterator {
4741e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        private final Cursor mEntityCursor;
4742e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        private volatile boolean mIsClosed;
4743e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
4744e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        private static final String[] PROJECTION = new String[]{
4745e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                Groups._ID,
4746e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                Groups.ACCOUNT_NAME,
4747e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                Groups.ACCOUNT_TYPE,
4748e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                Groups.SOURCE_ID,
4749e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                Groups.DIRTY,
4750e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                Groups.VERSION,
47517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                Groups.RES_PACKAGE,
4752c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Groups.TITLE,
4753c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Groups.TITLE_RES,
4754c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Groups.GROUP_VISIBLE,
4755c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Groups.SYNC1,
4756c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Groups.SYNC2,
4757f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                Groups.SYNC3,
47582e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov                Groups.SYNC4,
4759c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Groups.SYSTEM_ID,
4760c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Groups.NOTES,
4761c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Groups.DELETED,
4762c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Groups.SHOULD_SYNC};
4763c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4764c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_ID = 0;
4765c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_ACCOUNT_NAME = 1;
4766c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_ACCOUNT_TYPE = 2;
4767c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_SOURCE_ID = 3;
4768c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_DIRTY = 4;
4769c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_VERSION = 5;
4770c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_RES_PACKAGE = 6;
4771c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_TITLE = 7;
4772c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_TITLE_RES = 8;
4773c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_GROUP_VISIBLE = 9;
4774c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_SYNC1 = 10;
4775c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        private static final int COLUMN_SYNC2 = 11;
477600ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey        private static final int COLUMN_SYNC3 = 12;
4777d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        private static final int COLUMN_SYNC4 = 13;
477870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        private static final int COLUMN_SYSTEM_ID = 14;
477970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        private static final int COLUMN_NOTES = 15;
4780fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov        private static final int COLUMN_DELETED = 16;
478170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        private static final int COLUMN_SHOULD_SYNC = 17;
478270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
478370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        public GroupsEntityIterator(ContactsProvider2 provider, String groupIdString, Uri uri,
478470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                String selection, String[] selectionArgs, String sortOrder) {
4785d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            mIsClosed = false;
478670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
478767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            final String updatedSortOrder = (sortOrder == null)
47885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    ? Groups._ID
47895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    : (Groups._ID + "," + sortOrder);
4790619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4791619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            final SQLiteDatabase db = provider.mDbHelper.getReadableDatabase();
4792619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4793b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            qb.setTables(provider.mDbHelper.getGroupView());
4794f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            qb.setProjectionMap(sGroupsProjectionMap);
4795415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov            if (groupIdString != null) {
4796415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov                qb.appendWhere(Groups._ID + "=" + groupIdString);
4797415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov            }
4798b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            final String accountName = getQueryParameter(uri, Groups.ACCOUNT_NAME);
4799b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            final String accountType = getQueryParameter(uri, Groups.ACCOUNT_TYPE);
4800a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            if (!TextUtils.isEmpty(accountName)) {
4801f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                qb.appendWhere(Groups.ACCOUNT_NAME + "="
4802e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4803e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        + Groups.ACCOUNT_TYPE + "="
4804e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        + DatabaseUtils.sqlEscapeString(accountType));
4805b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            }
4806e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
4807f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    null, null, updatedSortOrder);
4808e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            mEntityCursor.moveToFirst();
48094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        }
4810d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4811d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        public void close() {
4812f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            if (mIsClosed) {
481349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                throw new IllegalStateException("closing when already closed");
481442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
481549d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mIsClosed = true;
481642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            mEntityCursor.close();
481742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        }
481842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
481942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        public boolean hasNext() throws RemoteException {
482042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            if (mIsClosed) {
482142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                throw new IllegalStateException("calling hasNext() when the iterator is closed");
482242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
4823f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
482442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            return !mEntityCursor.isAfterLast();
482542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        }
482642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
482749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        public void reset() throws RemoteException {
482842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            if (mIsClosed) {
482942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                throw new IllegalStateException("calling reset() when the iterator is closed");
483042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
483142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            mEntityCursor.moveToFirst();
4832d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann        }
4833d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann
483442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        public Entity next() throws RemoteException {
483542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            if (mIsClosed) {
4836d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                throw new IllegalStateException("calling next() when the iterator is closed");
483742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
4838d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann            if (!hasNext()) {
483942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                throw new IllegalStateException("you may only call next() if hasNext() is true");
484049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            }
484142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
484242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
484342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
484442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            final long groupId = c.getLong(COLUMN_ID);
4845d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4846d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            // we expect the cursor is already at the row we need to read from
4847d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            ContentValues groupValues = new ContentValues();
4848d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            groupValues.put(Groups.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
4849d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            groupValues.put(Groups.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
4850d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann            groupValues.put(Groups._ID, groupId);
4851f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            groupValues.put(Groups.DIRTY, c.getLong(COLUMN_DIRTY));
4852d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            groupValues.put(Groups.VERSION, c.getLong(COLUMN_VERSION));
4853b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            groupValues.put(Groups.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
4854b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            groupValues.put(Groups.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
4855fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            groupValues.put(Groups.TITLE, c.getString(COLUMN_TITLE));
4856fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            groupValues.put(Groups.TITLE_RES, c.getString(COLUMN_TITLE_RES));
4857b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            groupValues.put(Groups.GROUP_VISIBLE, c.getLong(COLUMN_GROUP_VISIBLE));
4858b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            groupValues.put(Groups.SYNC1, c.getString(COLUMN_SYNC1));
4859b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            groupValues.put(Groups.SYNC2, c.getString(COLUMN_SYNC2));
4860f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            groupValues.put(Groups.SYNC3, c.getString(COLUMN_SYNC3));
4861e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            groupValues.put(Groups.SYNC4, c.getString(COLUMN_SYNC4));
4862e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            groupValues.put(Groups.SYSTEM_ID, c.getString(COLUMN_SYSTEM_ID));
4863e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            groupValues.put(Groups.DELETED, c.getLong(COLUMN_DELETED));
4864e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            groupValues.put(Groups.NOTES, c.getString(COLUMN_NOTES));
4865e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            groupValues.put(Groups.SHOULD_SYNC, c.getString(COLUMN_SHOULD_SYNC));
4866e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            Entity group = new Entity(groupValues);
4867e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
4868e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            mEntityCursor.moveToNext();
4869e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
4870e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            return group;
4871e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
487208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood    }
4873f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
4874f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    @Override
487508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
487608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            String sortOrder) {
487708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        waitForAccess();
487808ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood
4879e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        final int match = sUriMatcher.match(uri);
4880e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        switch (match) {
4881d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case RAW_CONTACTS:
4882d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case RAW_CONTACTS_ID:
4883d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                String contactsIdString = null;
4884f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                if (match == RAW_CONTACTS_ID) {
4885d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                    contactsIdString = uri.getPathSegments().get(1);
4886d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                }
4887f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
4888d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return new RawContactsEntityIterator(this, uri, contactsIdString,
4889d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                        selection, selectionArgs, sortOrder);
4890d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case GROUPS:
4891d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case GROUPS_ID:
4892d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                String idString = null;
4893f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                if (match == GROUPS_ID) {
4894f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    idString = uri.getPathSegments().get(1);
4895f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                }
4896d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4897ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert                return new GroupsEntityIterator(this, idString,
4898ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert                        uri, selection, selectionArgs, sortOrder);
4899d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            default:
4900d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                throw new UnsupportedOperationException("Unknown uri: " + uri);
4901d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4902f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
4903f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
4904f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    @Override
4905f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public String getType(Uri uri) {
4906f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        final int match = sUriMatcher.match(uri);
4907f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        switch (match) {
4908f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            case CONTACTS:
4909f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            case CONTACTS_LOOKUP:
4910d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return Contacts.CONTENT_TYPE;
4911d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_ID:
4912d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_LOOKUP_ID:
4913d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return Contacts.CONTENT_ITEM_TYPE;
4914d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_AS_VCARD:
4915d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
4916d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case RAW_CONTACTS:
4917d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return RawContacts.CONTENT_TYPE;
49187a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa            case RAW_CONTACTS_ID:
49197a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa                return RawContacts.CONTENT_ITEM_TYPE;
4920108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            case DATA_ID:
4921108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
4922108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            case PHONES:
4923108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return Phone.CONTENT_TYPE;
4924108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            case PHONES_ID:
4925108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return Phone.CONTENT_ITEM_TYPE;
4926108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            case EMAILS:
4927108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return Email.CONTENT_TYPE;
4928d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case EMAILS_ID:
4929108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return Email.CONTENT_ITEM_TYPE;
4930108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            case POSTALS:
4931108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return StructuredPostal.CONTENT_TYPE;
4932108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            case POSTALS_ID:
4933108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return StructuredPostal.CONTENT_ITEM_TYPE;
4934108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            case AGGREGATION_EXCEPTIONS:
4935108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return AggregationExceptions.CONTENT_TYPE;
4936108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            case AGGREGATION_EXCEPTION_ID:
4937108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return AggregationExceptions.CONTENT_ITEM_TYPE;
4938108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            case SETTINGS:
4939108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return Settings.CONTENT_TYPE;
4940108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            case AGGREGATION_SUGGESTIONS:
4941108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return Contacts.CONTENT_TYPE;
4942d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case SEARCH_SUGGESTIONS:
4943d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return SearchManager.SUGGEST_MIME_TYPE;
4944d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case SEARCH_SHORTCUT:
4945b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
49464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
49474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                return mLegacyApiSupport.getType(uri);
4948415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        }
4949415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov    }
4950415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
4951a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private void setDisplayName(long rawContactId, String displayName, int bestDisplayNameSource) {
49524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (displayName != null) {
4953b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            mRawContactDisplayNameUpdate.bindString(1, displayName);
4954be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov        } else {
49552d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            mRawContactDisplayNameUpdate.bindNull(1);
4956b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov        }
4957b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(2, bestDisplayNameSource);
4958b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(3, rawContactId);
4959f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        mRawContactDisplayNameUpdate.execute();
496042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    }
4961f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
4962f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    /**
4963f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
4964b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov     */
4965be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
4966b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov        mDirtyRawContacts.add(rawContactId);
4967b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov    }
4968f481f22a9323fe338672f99b88b26c5f0725cd42David Brown
4969f481f22a9323fe338672f99b88b26c5f0725cd42David Brown    /*
4970508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey     * Sets the given dataId record in the "data" table to primary, and resets all data records of
4971b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov     * the same mimetype and under the same contact to not be primary.
497248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov     *
497348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov     * @param dataId the id of the data record to be set to primary.
497448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov     */
497548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
49769005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov        mSetPrimaryStatement.bindLong(1, dataId);
49779005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
497848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
497948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        mSetPrimaryStatement.execute();
498048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    }
498148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
498248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    /*
498348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov     * Sets the given dataId record in the "data" table to "super primary", and resets all data
498448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov     * records of the same mimetype and under the same aggregate to not be "super primary".
498548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov     *
4986b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov     * @param dataId the id of the data record to be set to primary.
4987b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov     */
4988b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
4989b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(1, dataId);
4990b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
4991b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
4992b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov        mSetSuperPrimaryStatement.execute();
4993b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov    }
4994c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4995c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    public void insertNameLookupForEmail(long rawContactId, long dataId, String email) {
4996c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (TextUtils.isEmpty(email)) {
4997c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return;
4998d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4999d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5000d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
5001d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (tokens.length == 0) {
500261efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            return;
500361efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov        }
50044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
50054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        String address = tokens[0].getAddress();
50067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        int at = address.indexOf('@');
500709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (at != -1) {
500809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            address = address.substring(0, at);
500909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
501009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
501109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        insertNameLookup(rawContactId, dataId,
501209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
501309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
501409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
501509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    /**
501609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
50178727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov     */
50188727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
50198727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
502009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            return;
502109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
502209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
502309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        insertNameLookup(rawContactId, dataId,
502409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
502509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
502609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
502709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
502809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            String title) {
502909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (!TextUtils.isEmpty(company)) {
503009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            insertNameLookup(rawContactId, dataId,
503109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
503209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
503309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (!TextUtils.isEmpty(title)) {
503409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            insertNameLookup(rawContactId, dataId,
503509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
503609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
503709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
503809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
503909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name) {
504009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name);
504109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
504209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
504309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    private interface NicknameLookupPreloadQuery {
504409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
504509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
504609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        String[] COLUMNS = new String[] {
504709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            NicknameLookupColumns.NAME
504809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        };
504909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
505009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        int NAME = 0;
505109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
505209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
505309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    /**
505409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov     * Read all known common nicknames from the database and populate a Bloom
505509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov     * filter using the corresponding hash codes. The idea is to eliminate most
5056f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * of unnecessary database lookups for nicknames. Given a name, we will take
5057f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * its hash code and see if it is set in the Bloom filter. If not, we will know
5058f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * that the name is not in the database. If it is, we still need to run a
5059f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * query.
5060f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * <p>
5061f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Given the size of the filter and the expected size of the nickname table,
5062f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * we should expect the combination of the Bloom filter and cache will
5063f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * prevent around 98-99% of unnecessary queries from running.
5064f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
506578fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov    private void preloadNicknameBloomFilter() {
5066f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNicknameBloomFilter = new BitSet(NICKNAME_BLOOM_FILTER_SIZE + 1);
5067f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
5068f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupPreloadQuery.TABLE,
5069f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NicknameLookupPreloadQuery.COLUMNS,
5070d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov                null, null, null, null, null);
5071f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        try {
5072f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            int count = cursor.getCount();
5073f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            for (int i = 0; i < count; i++) {
50742d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                cursor.moveToNext();
5075d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                String normalizedName = cursor.getString(NicknameLookupPreloadQuery.NAME);
5076d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                int hashCode = normalizedName.hashCode();
5077d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                mNicknameBloomFilter.set(hashCode & NICKNAME_BLOOM_FILTER_SIZE);
5078d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov            }
5079d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        } finally {
5080d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov            cursor.close();
5081d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        }
5082e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
5083916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5084916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5085e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    /**
5086e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov     * Returns nickname cluster IDs or null. Maintains cache.
50879a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov     */
50889a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    protected String[] getCommonNicknameClusters(String normalizedName) {
50899a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int hashCode = normalizedName.hashCode();
50909a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!mNicknameBloomFilter.get(hashCode & NICKNAME_BLOOM_FILTER_SIZE)) {
50919a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return null;
50929a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50939a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50949a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        SoftReference<String[]> ref;
50959a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String[] clusters = null;
50969a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        synchronized (mNicknameClusterCache) {
50979a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (mNicknameClusterCache.containsKey(normalizedName)) {
50989a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                ref = mNicknameClusterCache.get(normalizedName);
50999a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                if (ref == null) {
51009a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    return null;
51019a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                }
51024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                clusters = ref.get();
51037a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
51047a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
51057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (clusters == null) {
51077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            clusters = loadNicknameClusters(normalizedName);
51087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            ref = clusters == null ? null : new SoftReference<String[]>(clusters);
51097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            synchronized (mNicknameClusterCache) {
51107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                mNicknameClusterCache.put(normalizedName, ref);
51117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
51127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
5113f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        return clusters;
5114f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    }
51157a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private interface NicknameLookupQuery {
51177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
51187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        String[] COLUMNS = new String[] {
51207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            NicknameLookupColumns.CLUSTER
51217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        };
51227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        int CLUSTER = 0;
51247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
51257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    protected String[] loadNicknameClusters(String normalizedName) {
51277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
51287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        String[] clusters = null;
51297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupQuery.TABLE, NicknameLookupQuery.COLUMNS,
51307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
51317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                null, null, null);
51327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        try {
51337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            int count = cursor.getCount();
51347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (count > 0) {
51357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                clusters = new String[count];
51367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                for (int i = 0; i < count; i++) {
51377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                    cursor.moveToNext();
51387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                    clusters[i] = cursor.getString(NicknameLookupQuery.CLUSTER);
51397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                }
51407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
51417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        } finally {
51427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            cursor.close();
51437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
51447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return clusters;
51457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
51467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
51487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
51507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            super(splitter);
51514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
51524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
51534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        @Override
5154b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
5155b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                String name) {
5156b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
5157b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5158b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
51594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        @Override
51604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
5161b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return ContactsProvider2.this.getCommonNicknameClusters(normalizedName);
5162b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5163b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5164caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
51655e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /**
51665e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
51675e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar     */
51685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
51695e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, rawContactId);
51705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, dataId);
51715e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, lookupType);
51725e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 4, name);
51735e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        mNameLookupInsert.executeInsert();
51745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
51755e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5176caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    /**
5177caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
5178caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov     */
51795f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov    public void deleteNameLookup(long dataId) {
5180caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupDelete, 1, dataId);
5181caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        mNameLookupDelete.execute();
5182caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
5183caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
51846f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
5185caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        sb.append("(" +
51866f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
5187caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
5188f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
518973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
519073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
519173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                " WHERE normalized_name GLOB '");
519273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
5193bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN("
5194bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov                + NameLookupType.NAME_COLLATION_KEY + ","
5195bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov                + NameLookupType.EMAIL_BASED_NICKNAME + ","
5196bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov                + NameLookupType.NICKNAME + ","
519773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                + NameLookupType.ORGANIZATION + "))");
519873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    }
519973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
520073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
520173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5202627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
5203627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        return sb.toString();
5204627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
5205627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
520673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam,
520773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            String limit) {
520873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), limit,
5209627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                true);
5210627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
5211627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
5212627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
5213627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            String limit, boolean allowEmailMatch) {
521473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        sb.append("(" +
521573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                "SELECT DISTINCT " + NameLookupColumns.RAW_CONTACT_ID +
521673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
521773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
521873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                " GLOB '");
521973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        sb.append(normalizedName);
522073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
5221627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                + NameLookupType.NAME_COLLATION_KEY + ","
5222b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov                + NameLookupType.NICKNAME + ","
5223d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                + NameLookupType.ORGANIZATION);
5224f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (allowEmailMatch) {
5225f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
5226f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5227f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        sb.append(")");
5228f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5229f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (limit != null) {
5230f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            sb.append(" LIMIT ").append(limit);
5231f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5232f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        sb.append(")");
5233f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5234f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5235f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
5236f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
5237f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
5238f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5239f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (selectionArgs == null) {
5240f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return new String[] {arg};
5241f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
5242f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            int newLength = selectionArgs.length + 1;
5243f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            String[] newSelectionArgs = new String[newLength];
5244f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            newSelectionArgs[0] = arg;
5245f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5246f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return newSelectionArgs;
5247f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5248f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5249f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5250f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String[] appendProjectionArg(String[] projection, String arg) {
5251f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (projection == null) {
5252f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
5253f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5254f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final int length = projection.length;
5255f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String[] newProjection = new String[length + 1];
5256f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        System.arraycopy(projection, 0, newProjection, 0, length);
5257f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        newProjection[length] = arg;
5258f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return newProjection;
5259f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5260f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5261f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    protected Account getDefaultAccount() {
5262f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
5263f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        try {
5264f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
5265f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
5266f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
5267f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return accounts[0];
5268f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
52695fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa        } catch (Throwable e) {
52705fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
52715fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa        }
52725fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa        return null;
52735fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa    }
52745fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
52755fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa    protected boolean isWritableAccount(Account account) {
52765fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa        IContentService contentService = ContentResolver.getContentService();
52775fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa        try {
52785fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
52795fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
5280f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                        account.type.equals(sync.accountType)) {
5281f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                    return sync.supportsUploading();
5282f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                }
5283f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5284f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } catch (RemoteException e) {
5285f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
5286f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5287f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return false;
5288f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5289f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5290f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
5291f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
5292f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5293f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
5294f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5295f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5296f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5297f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5298f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5299f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
5300f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
5301f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5302f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
53035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
53040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        index += parameter.length();
53050dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53060dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
53070dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
53080dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
53090dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53100dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
53110dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            boolean ignoreCase) {
53120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int length = value.length();
5313bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
53140dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
53150dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
53160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53170dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    /**
53180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
53190dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov     */
53200dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
532149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        String query = uri.getEncodedQuery();
53220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (query == null) {
53230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return null;
53240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
53250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int queryLength = query.length();
53270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int parameterLength = parameter.length();
53280dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        String value;
53300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int index = 0;
53310dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        while (true) {
53320dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            index = query.indexOf(parameter, index);
53330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            if (index == -1) {
53340dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                return null;
53350dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
53360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53370dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            index += parameterLength;
53380dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53390dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            if (queryLength == index) {
53400dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                return null;
5341bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            }
5342bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
53430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            if (query.charAt(index) == '=') {
53440dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                index++;
53450dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                break;
53460dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
53470dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
53480dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int ampIndex = query.indexOf('&', index);
53500dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (ampIndex == -1) {
53510dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            value = query.substring(index);
53520dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } else {
53539a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            value = query.substring(index, ampIndex);
53549a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
53559a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
53569a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return Uri.decode(value);
53579a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
53589a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov}
53599a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov