ContactsProvider2.java revision 6d9702cec82fd27a1c3093c64df9dcc22744899a
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.common.content.SQLiteContentProvider;
2053214b3ed12b0ff9cb589b6559311f2ac142f2e3Bjorn Bringertimport com.android.common.content.SyncStateContentProviderHelper;
215b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport com.android.providers.contacts.ContactAggregator.AggregationSuggestionParameter;
2297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
2397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
2497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
2697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
2797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
2897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
2997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
3097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3697fd30388bd6530f86679510cd7b43b9c518bcefDmitri 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.android.vcard.VCardComposer;
4197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardConfig;
4297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
4397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
4497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
4597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
46b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
47caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
485b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
49bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.Notification;
50bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.NotificationManager;
51bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.PendingIntent;
52c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
53568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
54568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
556ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
5635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
5767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
5867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
59627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
60bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.Intent;
61568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
623d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
63627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
6467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
65f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
66e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CrossProcessCursor;
674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
68e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CursorWindow;
69ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
70ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
7109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
7209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
7408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
77d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
78bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Binder;
796ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
80bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Handler;
81bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.HandlerThread;
82bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Message;
83ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
84bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Process;
85b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
8615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.os.StrictMode;
870dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
880e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
893d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
90508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
913de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
92b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
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.Note;
9897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
9997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
10097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
10197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
10297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
103ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
1043de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1055b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1063de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
107d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
1083de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
109bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1103de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
11109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1123de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
113916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1143de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
11582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
11697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
11797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
11897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
119a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
1209a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport android.telephony.TelephonyManager;
121a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
122c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
124d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
125b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
126d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
127d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
12842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
13142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
132b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1330e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
135622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
136b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1370e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
138ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1445b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
145caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
146bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
147bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
148bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
15015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_INITIALIZE = 0;
15115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
15215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS = 2;
15315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
15415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
15515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
15615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 6;
15715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 7;
158fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 8;
159619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1633d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
164b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1673d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
168b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
169b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
17051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
1713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1720dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
1730dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
1740dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
1750e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1760e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
177a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
179dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov    private static final String TIMES_CONTACTED_SORT_COLUMN = "times_contacted_sort";
1805e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
181d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
182dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov            + TIMES_CONTACTED_SORT_COLUMN + " DESC, "
1839b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
184d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
185d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
186d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
187d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1886e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
1899b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
1909b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1919b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
1929b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
1936e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
1949b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
1959b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1969b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
1979b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
198de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
199de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
200d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
201d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
204a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
209a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
210f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
21142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private static final int CONTACTS_AS_MULTI_VCARD = 1011;
2122149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_DATA = 1012;
2132149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_DATA = 1013;
214a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_ENTITIES = 1014;
215a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ENTITIES = 1015;
216a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1016;
2174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2185ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
22146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
2224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2236bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2246bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
225ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
22648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
22748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
22848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
22948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
23048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
23148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
23248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
23348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
234a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2356bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2366bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
237b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
238b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
239b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
24082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
24182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
24331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
24431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
245eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
246eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
247ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
248ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
249ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
250ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
25135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
252b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
25335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
254c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
255c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
256c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
26246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
26346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
26409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
26509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
266d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
267d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
268d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
2697a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
2707a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
271dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
272dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
273dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
274dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
275dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
276dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE
277dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
278dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
279dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
280dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
281dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
282dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
283dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
284dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND "
285dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + Groups.AUTO_ADD + " != 0";
286dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
287dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
288dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
289dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
290dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
291dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
292dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
293dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
294dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
295dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
296dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
297dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
298e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    public class AddressBookCursor extends CursorWrapper implements CrossProcessCursor {
299e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final CrossProcessCursor mCursor;
300e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final Bundle mBundle;
301e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
302e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public AddressBookCursor(CrossProcessCursor cursor, String[] titles, int[] counts) {
303e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            super(cursor);
304e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor = cursor;
305e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle = new Bundle();
306e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
307e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
308e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
309e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
310e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
311e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public Bundle getExtras() {
312e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mBundle;
313e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
314e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
315e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
316e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public void fillWindow(int pos, CursorWindow window) {
317e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor.fillWindow(pos, window);
318e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
319e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
320e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
321e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public CursorWindow getWindow() {
322e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.getWindow();
323e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
324e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
325e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
326e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public boolean onMove(int oldPosition, int newPosition) {
327e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.onMove(oldPosition, newPosition);
328e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
329e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    }
330e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
331d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
332f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
333f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
334f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
33567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
33667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
3376cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
3383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
339f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
340ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
341ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
342d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
34367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
344d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
345ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
347f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
34819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
34919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
35019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
351ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
352ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
353ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
35419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
35519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
35619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
357ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
358ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
35919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
36019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
361c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
362caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
36371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
36471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
36571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
36671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
36771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
36871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
36971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
37071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
37171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
37271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
37371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
37471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
37571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
37671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
377a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
378a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
379a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
380a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
381a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
382a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
383a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
384a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
385a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
386a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
387a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
388a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
389c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
390c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
391c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
392c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
393c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
394c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
395c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String TIME_SINCE_LAST_CONTACTED =
396c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            "(strftime('%s', 'now') - " + Contacts.LAST_TIME_CONTACTED + "/1000)";
397c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
398c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
399c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
400c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Within the starred/unstarred groups - three buckets: very recently contacted, then fairly
401c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
402c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * of times contacted. If all else fails, alphabetical.  (Super)primary email
403c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * address is returned before other addresses for the same contact.
404c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
405c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
406c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            "(CASE WHEN " + Contacts.STARRED + "=1 THEN 0 ELSE 1 END), "
407c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + "(CASE WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_CURRENT + " THEN 0 "
408c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + " WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_RECENT + " THEN 1 "
409c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + " ELSE 2 END),"
410c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Contacts.TIMES_CONTACTED + " DESC, "
411c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Contacts.DISPLAY_NAME + ", "
412c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Data.CONTACT_ID + ", "
413c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Data.IS_SUPER_PRIMARY + " DESC";
414c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
415916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
416916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
417916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
418916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
419916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NICKNAME + "," +
420916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_SHORTHAND + "," +
421f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.ORGANIZATION + "," +
422f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.NAME_CONSONANTS;
423916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
424f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
425f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
426f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
427f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
428f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
429f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
430f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
431f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
432f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
433f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
434f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
435f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
436f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
437f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
438f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
439916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
440f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
441f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
442f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
443f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
444f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
445f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
446f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
447f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
448f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
449f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
450f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
4513d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
4523d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
453f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
454f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
455f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
456f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
457f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
458cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
459f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
460f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
461f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
462f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
463f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
464f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
465f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
466f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
467f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
468f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
469f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
470f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
471f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
472f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
473f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
474f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
475f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
476f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
477f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
478f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
479f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_MIMETYPE)
480f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA_ID)
481f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA1)
482f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA2)
483f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA3)
484f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA4)
485f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
486f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
487f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
488f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
489f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
490f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
491f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
492f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
493f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
494f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
495f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
496f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
497f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
498f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
499f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
500f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
501f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
502f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
503f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
504f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
505f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
506f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
507f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
508f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
509f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
510f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
511f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
512f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
513f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
514f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
515f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
516f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
517f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
518f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
519f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
520f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
521f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
522f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
523f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
524f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
525f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
532f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
533f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
559038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
564e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
573916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
578916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5795e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, Contacts.TIMES_CONTACTED)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
590f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
596ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
618a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.IS_RESTRICTED)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
631a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.IS_RESTRICTED)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
6813d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
6823d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
6892530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
692ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
698f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
701f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
704f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
705f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
709f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
710c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
712f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
716f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
717ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ")")
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Contacts.HAS_PHONE_NUMBER + ")")
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
734373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
742eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
78082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
8021b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
811d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
817f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
821778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
822778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
823f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
8247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
8259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
8269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
8279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
8289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
8299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
8309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
8312526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
8322526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
833bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
834bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
835bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
836bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
83751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
8389a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
8399a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
8409a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
841f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
8421129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
8431129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
8442526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
8452526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
846f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
847f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
8484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
8494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
850a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
851d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
852d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
853a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
854a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
8553653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
8563653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
8572d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
8582d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
859a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
860c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
8615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
8625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
8632149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
8645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
8652149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
8662149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
867a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
868a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
869a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
870a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
871f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
87242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
87342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
8745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
875ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
876ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
8775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
8783653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
8795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
8805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
8815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
88246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
88346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
88446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
885b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
8864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
8874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
888ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
88948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
8905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
891ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
8924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
89348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
8941dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
8955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
8965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
8974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
898ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
89948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
9001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
901ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
902ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
903ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
904ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
90535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
906b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
907b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
90835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
909a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
910b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
911b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
912b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
913b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
9144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
915eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
916eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
91782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
91882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
9191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
920c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
921c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
922c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
923c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
9242d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
925c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
926c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
9271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
9281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
9291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
9301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
9311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
9321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
9331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
9341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
93509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
93609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
937d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
938d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
939d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
9407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
9417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
94219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
94319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
944d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
945d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
946d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
947d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
948d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
949d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
950d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
951d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
952d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
9534458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
9544458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
955d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
9563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
957ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
958ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
959ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
960e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
961ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
962ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
963ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
964ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
965ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
966a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
967e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
968e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
969e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
970e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
971e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
9723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
973b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
97431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
9754097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
976f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
977315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
978622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
979622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
98072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
981622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
982f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
983a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
984d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
985f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
986a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
98720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
98873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
98920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
99009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
9913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
99209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
99315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
99415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
99515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
996bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
99773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
998d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private TransactionContext mTransactionContext = new TransactionContext();
999de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
10001a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
10011a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
100281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
100381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
10044cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
10053826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1006d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1007bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1008bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1009bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
10104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
10114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1012de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1013ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1014ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1015ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1016ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1017ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1018ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1019ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
102035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1021ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
102215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
102315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
102415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
1025b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
102672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1027a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
102865ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1029bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
103015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
103115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
103272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1033bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1034bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1035bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1036bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1037bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1038bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1039bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1040bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1041bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
10422a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
104315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1044bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1045bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1046bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1047bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
1048bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
104915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
10503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
105149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
10524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
10534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1054767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
105551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
105651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
105704b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
105815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
105915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
10604cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
106104b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
10624cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
10634cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
106451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
1065cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
10665b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
106715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
10685b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1069f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
10705b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1071bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
1072bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1073bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE,
10746d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForEmail(context, mDbHelper, mContactAggregator));
1075bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
10766d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForIm(context, mDbHelper, mContactAggregator));
1077bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE,
10786d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForOrganization(context, mDbHelper, mContactAggregator));
1079bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE,
10806d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoneNumber(context, mDbHelper, mContactAggregator));
1081bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
10826d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNickname(context, mDbHelper, mContactAggregator));
1083bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
10846d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredName(context, mDbHelper, mContactAggregator,
1085bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
1086bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
10876d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredPostal(context, mDbHelper, mContactAggregator,
1088bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
1089bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE,
10906d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForGroupMembership(context, mDbHelper, mContactAggregator,
1091bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
1092bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE,
10936d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoto(context, mDbHelper, mContactAggregator));
10946d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov        mDataRowHandlers.put(Note.CONTENT_ITEM_TYPE,
10956d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNote(context, mDbHelper, mContactAggregator));
1096bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1097bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1098bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1099bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1100bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1101bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1102bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1103bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1104bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1105bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1106bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1107bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1108bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1109bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1110bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1111bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1112bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1113bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1114bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
111515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
111615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
111715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
111815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
111915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
112015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
112115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
112215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1123bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
112415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
112515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1126bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1127bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1128bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1129bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1130bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1131bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1132bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1133bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1134bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1135bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1136bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1137bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
113815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
113915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
114015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
114115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
114215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
114315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
114415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
1145bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
1146bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1147bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1148bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1149bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1150bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1151bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1152bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1153bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1154bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1155bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1156fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1157fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1158fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1159fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1160fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1161bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1162bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1163bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1164bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1165bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1166bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1167bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1168bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1169bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1170bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1171bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1172bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1173bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1174bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1175bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1176bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1177bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1178bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1179bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
11804cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
11814cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
118253fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
11833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
11843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
11854f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
11864f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
11874f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1188fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
11894cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
119051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
119151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
119251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
119351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
119451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
119551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
119651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
119751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1198bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1199f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1200f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1201f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1202f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1203f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1204f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
120551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
120651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
120751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
120851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
120951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
121051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
121151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
121251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
121351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
1214bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, currentLocale);
1215bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1216bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1217bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
121851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1219fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1220fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1221fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1222fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1223fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1224fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1225fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1226fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
1227fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1228fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
1229fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1230fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1231fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
1232fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1233fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
1234fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1235fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1236fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1237fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1238fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1239bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1240bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
124151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
124251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
12433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
12443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
12453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
12463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
12473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
12483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
12493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mContactsAccountCount == 0
125049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                && DatabaseUtils.queryNumEntries(mDbHelper.getReadableDatabase(),
125149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                        Tables.CONTACTS, null) == 0) {
12523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
12533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
12543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
12553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
12563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
12573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
125831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1259de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1260b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1261b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
126231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
126331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1264013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1265013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1266013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1267013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
12685df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
12695df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
12705df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
12715df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
12725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1273ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
127472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
127572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
127672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
127772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
12785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
12795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
12805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
12815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
12823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
1283b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1284b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
12853d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
12863d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1287568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1288568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1289568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1290568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1291568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1292bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1293568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1294bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1295bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1296bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1297568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1298bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1299bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, mCurrentLocale);
1300bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1301568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1302bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1303bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1304bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1305bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1306bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1307bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1308568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1309568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1310bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1311bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1312bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1313bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1314bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1315bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1316bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1317bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1318b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
1319b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1320b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1321bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1322bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1323bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1324bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1325bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1326bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1327bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1328bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1329bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1330bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1331bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1332bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1333bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1334bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1335bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1336bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1337bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1338bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1339bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1340bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1341bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1342bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1343bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1344bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1345bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1346bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1347bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1348bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1349bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
13503d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
13513d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
13523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1353568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
13540e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
13553d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
13563d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1357bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1358bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1359bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1360bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1361bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1362bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
13633d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
13643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
13653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1366bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1367bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
13683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
13693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1370a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1371a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1372a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1373a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1374b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
13753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1376a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1377a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1378568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
137915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1380568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1381568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1382568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1383568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1384568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
138515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
138615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
138715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
138815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
138915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
139015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
139115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
139215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
139315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
139415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
139515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1396ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1397568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1398568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1399568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1400568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1401568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
140215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1403568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1404568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1405568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1406568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1407568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
140815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
1409bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
1410bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
1411bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
1412bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
1413bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
1414bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
1415bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
1416bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1417bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
1418bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
1419bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
1420bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
1421bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1422bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
142315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1424568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1425568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1426568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1427568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1428568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
142915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1430568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1431568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1432568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1433568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1434568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1435568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
143615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1437568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1438568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1439568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
14404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
14417b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
14427b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
14437b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        return super.bulkInsert(uri, values);
14447b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
14457b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
14467b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
1447285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1448bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1449b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1450b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1451285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
14521ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1453d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1454b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1455b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1456285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1457285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1458285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
14591129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1460bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1461b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1462b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1463285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1464b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
14651ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
14661a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
14671a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1468b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
14691a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
14703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
14713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
14723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
14733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
14743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
1475b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1476b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1477b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1478bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1479b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1480b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
14811129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1482d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (long rawContactId : mTransactionContext.getInsertedRawContactIds()) {
14837e2635fa663312adb2bc9d04f50a6bb54c6cc5f4Dmitri Plotnikov            mContactAggregator.updateRawContactDisplayName(mDb, rawContactId);
1484d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
1485285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1486b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1487d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> dirtyRawContacts = mTransactionContext.getDirtyRawContactIds();
1488d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
1489a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1490a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1491d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
1492a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1493a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
1494a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1495a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1496d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> updatedRawContacts = mTransactionContext.getUpdatedRawContactIds();
1497d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
1498a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1499a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1500d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
1501a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1502a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
1503b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1504b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1505f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        Set<Long> staleRawContacts = mTransactionContext.getStaleSearchIndexRawContactIds();
1506f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        if (!staleRawContacts.isEmpty()) {
1507f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleRawContacts);
1508f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        }
1509f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov
1510d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (Map.Entry<Long, Object> entry : mTransactionContext.getUpdatedSyncStates()) {
1511b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
15129d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
15139d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
15149d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
15159d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
1516b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1517b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1518d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1519b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1520b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1521a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1522a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1523a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1524a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1525d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
1526b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1527a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1528b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1529a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1530a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1531285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1532285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1533285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1534cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
153581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
153681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
153781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
153881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
153981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
154081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
154181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
1542cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
1543568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
154451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
15453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
15463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
15473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
15483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
154951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
155051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1551f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
15523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
15533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
15546d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
15556d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                    getContext(), mDbHelper, mContactAggregator, mimeType);
15563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
15573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
15583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
15593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
15603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1562de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
1563bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
15641129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
1565b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1566f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1567f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
1568f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
1569f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1570a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
1571a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
157235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1573a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
157435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
1575b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
157635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
157735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1578d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
1579d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
15806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
15816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
15826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
15835ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
1584dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                id = insertRawContact(uri, values, callerIsSyncAdapter);
1585f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1586a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1587a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1588a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
15895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
15905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
1591f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1592f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1593a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1594a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1595a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1596a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
1597f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1598f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1599a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1600a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1601a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1602ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1603f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
1604f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1605ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1606ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1607ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1608eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
16095aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
161043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
1611eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
1612eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
1613eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
161482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
161582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
16161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
16171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
16181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1619a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
162081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
1621f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
1622a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1623a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
16247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
16257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
16267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
16277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1628de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
1629a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1630a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1631a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1632e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
1633e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
1634e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
1635e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
1636e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
1637e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
1638e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
1639e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
1640e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
1641e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
1642e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
1643e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
1644e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
16457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
1646e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
1647f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
1648f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
1649e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
1650f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1651f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1652f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
1653e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
1654e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
1655e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1656e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
1657e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
1658fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
1659fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
1660e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
1661e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1662e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
1663e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
1664e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
1665e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
1666e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1667e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
1668e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
1669e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
1670e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
1671e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
1672fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
1673fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
1674e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
1675e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
1676e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
1677f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
1678f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
1679e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
1680f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
1681f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
1682e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
1683e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
1684f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
1685f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1686e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
1687f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
1688f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
1689f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
1690f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
1691035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1692f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1693e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
16947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
16957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
1697d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
16986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
16996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
17006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
17016bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
1702d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
1703de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
17046bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
17056bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
17066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
1707a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
1708a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1709f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
1710f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
1711dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
1712a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1713a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1714dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
1715f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
1716f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
1717f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
1718f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1719e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
17207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17213d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
17223d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
1723f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
17243d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
17253d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1726f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
1727f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
1728f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
1729f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
1730f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
1731f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
1732285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1733285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
1734d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactInserted(rawContactId, account);
1735f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1736dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
1737dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
1738dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
1739dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
1740dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
1741dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1742dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1743dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
17443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
1745023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
1746a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1747a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1748dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
1749dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
1750dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
1751dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
1752dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
1753dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1754dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1755dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1756dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
1757dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
1758dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
1759dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
1760dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
1761dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
1762dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
1763dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
1764dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1765dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
1766dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
1767dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
1768dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1769dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1770dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1771dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
1772dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
1773dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
1774dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
1775dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
1776dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
1777dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
1778dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
1779dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1780dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1781dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1782dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1783dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
1784dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
1785dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
1786dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
1787dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
1788dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
1789dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
1790dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1791dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1792dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
1793dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
1794dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
1795dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
1796dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
1797dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
1798dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1799dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1800a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1801a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
1802a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1803a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1804a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1805a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1806f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
1807a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
1808de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
1809de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
181067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
1811de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
181220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1813de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
1814de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
1815de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
1816b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
1817de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
1818de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
1819508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
1820de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
1821de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
1822de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
1823de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
1824de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
18254097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
1826b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
1827de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
1828a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1829a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
1830d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        id = rowHandler.insert(mDb, mTransactionContext, rawContactId, mValues);
1831f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
1832d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.markRawContactDirty(rawContactId);
1833a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1834d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactUpdated(rawContactId);
1835a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
18364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
18374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1838ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
18397e2635fa663312adb2bc9d04f50a6bb54c6cc5f4Dmitri Plotnikov        mContactAggregator.updateRawContactDisplayName(db, rawContactId);
1840d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
1841d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
18429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
184320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
184420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
1845f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
184620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
184720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1848de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
1849de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
1850f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS,
1851f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
1852de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
1853de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
1854f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
1855f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
1856a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
1857d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                count += rowHandler.delete(mDb, mTransactionContext, c);
1858f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
1859d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                    mTransactionContext.markRawContactDirty(rawContactId);
186088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
186120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
186220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
1863de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
186420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
186520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
186620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
186720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
186820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
186988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
187088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
187188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
187220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
1873f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
187488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
187588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
18764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
1877f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
18784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
1879f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
188020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
188120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
188220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
188320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
188420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1885f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
188620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
188720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
188820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
188920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
189020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
189120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
189220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
189320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
189420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
18957a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
189620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
189720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
189820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1899a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
1900d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return rowHandler.delete(mDb, mTransactionContext, c);
190120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
190220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
190320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
190420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
190520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
190620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
1907ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
1908ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
1909f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
1910f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
1911f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
1912f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1913e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
1914ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1915ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
1916f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
191767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
1918f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
191967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
1920f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
1921ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1922dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
1923dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
1924dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
1925dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1926f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
1927f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
192873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
192973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1930f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
1931ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
1932dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
1933dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
1934dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
1935dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
1936dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
1937dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
1938dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
1939dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
1940dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
1941dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
1942dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
1943dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
1944dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1945dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
1946dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
1947dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
1948892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
1949892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
1950892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
1951892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
1952892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
1953d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mTransactionContext.markRawContactDirty(rawContactId);
1954892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
1955dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
1956892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
1957892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
1958dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1959dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1960dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1961f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
19621a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
1963ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
1964ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
1965ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
1966ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1967ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
19685aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
1969e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
19705aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
19711a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
19721a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
1973e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
19741a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
1975e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
1976e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
1977e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
1978ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
197982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
19801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
198182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
198282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
19830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
19844dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
19854dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
19860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
198782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
19884dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
19894dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
19904dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
19914dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
19921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
19931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1994dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
1995dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
199682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
1997f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
19982526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
1999dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2000dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2001dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
20022526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
20032526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
20041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2005dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2006dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
20070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
20080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
20090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
20100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2011dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2012dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2013dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
20142a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mDbHelper.getMimeTypeIdForIm());
2015dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
20162a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mDbHelper.getMimeTypeIdForEmail());
2017f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2018f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2019f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2020f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2021f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2022f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2023f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
20242526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
20252526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
20262526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
20272526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
20282526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
20292526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
20302526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
20312526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
2032dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
20332526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
20342526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2035dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
20362526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
20372526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
2038dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
20392526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
20402526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
20412526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
20422526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
20432526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
20442526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
2045dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
20462526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
20472526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2048dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2049dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
20501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
205182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
20522526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
20532526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
2054dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
205570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
2056f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
205770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
20581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
20591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2060de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
20612526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
20624394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
20631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
206467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
20655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2066e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
20671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
20681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
20691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
20701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
20711f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
207231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
207331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
207431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
20751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
20761f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
207782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2078a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2079a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2080a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2081a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2082a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2083a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2084a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
208582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2086a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2087a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
208882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
208982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
209082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
209182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
209282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2093a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
209482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
209582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
2096aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
2097aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
20981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2099a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2100a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2101a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2102e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
21030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
210482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
210582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
21060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
21070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
21080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
21090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
21100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
21110a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
21120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
21130a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
21140a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
21150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
21160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
21170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2118a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
211978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteStatusUpdate(dataId);
212082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
212182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
212278fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.replaceStatusUpdate(dataId, timestamp, status, resPackage, iconResource,
212378fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
2124a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
212578fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.insertStatusUpdate(dataId, status, resPackage, iconResource,
212678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
2127e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2128e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2129bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2130a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2131f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
2132a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2133a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2134a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
21351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
21361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
21374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2138de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2139bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2140b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2141b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2142b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2143f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2144f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2145508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2146508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
214735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2148b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
214935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2150b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2151b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2152b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2153b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2154b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2155b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2156cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2157cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2158cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2159cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2160cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2161d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2162d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2163dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
21646bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
21656bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
21669fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
21672e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
21682e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
21692e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
2170fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2171fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
21722e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
21732e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
21742e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
2175dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
21762e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
21772e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
21789fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
21799fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
21809fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
21819fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
21829fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
21839fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
2184a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
21859fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
21869fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
21879fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
21889fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
21899fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
21909fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
21919fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
21929fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
219360de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
21949fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
21959fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
21969fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
21979fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
21989fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
21999fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
2200dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
22019fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
22029fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
22039fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
22049fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
22059fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
22069fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
22079fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
22089fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
22099fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
22109fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
22112971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
22122971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
2213fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
2214fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
2215e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
22162971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
22172971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
22182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2219fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
2220fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
2221fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
22222971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
22232971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
22242971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
22252971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
22262971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
22272971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
22282971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
22295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
22302971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2231fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
2232fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
2233508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2234508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
223520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2236f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2237944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2238f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
223920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
224020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
224148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
224248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
224348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
224448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2245508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2246f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
22474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
22484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
2249ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2250ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2251ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2252f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
22535aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
22542971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
22552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
22562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
22572971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
22582971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2259e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
22602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
22612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
22625aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
22632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
22642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
22652971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
22662971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
226781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2268f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
226981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
22702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2271508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2272508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2273eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
227443880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2275e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
2276eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2277eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
227882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
22790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
22801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
22811f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
228281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
228381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
22843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
228581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2286508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
22874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
22884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
22891c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
2290ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2291b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
229294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2293de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
229494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
229594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
229694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
229794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2298f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2299de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
230094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
230194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
230294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2303f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2304de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
230594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
230694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
23071a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
230894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
230994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
231094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
23115aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2312e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
23131a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
2314e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2315e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2316e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2317dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
231896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
2319cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
232096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
232196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
2322cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2323cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2324cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2325dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
2326cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2327cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2328cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2329cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2330cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
23313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
23323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2333cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2334cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2335cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2336fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
23373389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
23383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
23393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2340f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
234114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2342fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
2343fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
2344fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
234533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2346b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2347dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
234833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
234933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
235033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
23510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
23529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
23539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
23549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
23559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
23569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
23579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
23589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
23599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
23600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
23610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2362dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
236381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
236481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2365cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2366cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2367cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2368cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2369cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2370cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2371dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
2372cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2373cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
23744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2375de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2376de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2377bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2378b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2379b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2380b5a4add17815167d20a90645779df34cdf45280dFred Quintana
238135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
238200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
238300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2384b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2385b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
23861129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
2387d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.syncStateUpdated(rowId, data);
2388b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2389b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2390b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2391f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2392f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
239300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
239435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2395b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2396b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2397b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2398b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2399b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2400b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2401b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2402b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2403b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2404b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2405b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
240635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2407d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2408dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
240900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
241000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
241100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2412d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2413dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
2414c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2415c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2416c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
24172e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
24182e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
24192e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
24202e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
24212e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
2422fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2423fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
24242e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
24252e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
24262e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
2427dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
24282e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
24292e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
24302e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
24317d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
24327d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
24337d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
24347d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
24357d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
24367d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
24377d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
24387d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
24397d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
24407d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
244120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2442944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2443f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
244481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2445f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
244681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
244720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
244820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2449c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
245048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
245148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
245248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
245348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2454f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
245581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2456f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
245781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
245800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
245900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
24607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24615ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
24625ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
2463dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
24647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
24657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
24667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
246833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
24694529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
24704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
24714da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
2472dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
2473dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
24744529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
24754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
2476dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
2477dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
24784529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
24797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
24807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
24817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2482ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
24835aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
2484f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
248581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2486f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
248781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2488ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2489ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2490ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2491ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2492ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
24934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
24944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
249573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
24965aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
24975aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
249881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2499f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
250081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2501ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2502ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2503ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2504127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
2505de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
2506b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2507b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2508b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2509eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
2510e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
2511e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
251243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2513eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2514eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2515eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
25169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
25179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
25189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
25199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
25209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
252172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
2522bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
252372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
2524d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
2525d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
2526d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
252781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
252881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2529f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
253081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
253100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
253200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
253300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
25344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
25354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
25369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
25379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
25389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
25399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
25409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
25419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
25429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
25439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
25449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
25459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
25469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
25479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
25489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
25509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
25519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
25529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
25539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
25549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
25559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
25569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
25579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
25589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
25599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
25619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
25629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
25639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
25649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
25659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
25669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
25679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
25689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
25699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
25709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
25729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
25739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
25749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
25759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
25769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
25779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
25789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
25799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
25809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
25819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
25829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
25839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
25849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
25859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
25879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
25889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
25899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
2590aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
2591aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
25929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
25939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
25949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25955aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
2596f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
259773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2598ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2599ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
260073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
2601f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
260273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
260373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
260473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
260573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
260673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
260773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
260873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
260973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2610ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
26111a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
26121a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
261394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
26146ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
26151129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
26166ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
2617e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
26186ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
26196ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
26206ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
26216ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
26226ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
26236ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
26246ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
26256ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
26266ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
2627ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
26286ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
26296ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
26306ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
26316ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
26326ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
26336ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
26346ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
26356ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
263694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
263794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
263894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
2639b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
2640b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
2641e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
26421a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
26431a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2644e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2645e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2646e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2647e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2648dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
2649dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
26504529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
26514529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
26524529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
26534529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
265473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
265597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
265697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
265797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
265897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
265997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
26604529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
2661b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
266251bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
26634529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
26644529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
26654529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
26664529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
2667dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
26684529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
26694529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
26704529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
26714529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
26724529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
26734529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
26744529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
26754529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
26764529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
2677dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
2678dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
267996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
268096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
268119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
268219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
268319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
2684ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
2685ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
268619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
268719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
268896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
268919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
269019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
269119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
2692ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
2693ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
269419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
269519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
269619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
269719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
269819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
269919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
270019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
2701f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
270296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
27035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
2704f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
2705f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
2706f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
2707f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
2708f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
2709f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
271069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
2711f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
2712f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
2713433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
2714dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
2715dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
2716dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
2717dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
27184529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
2719dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2720dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
2721dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
2722dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
2723dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
2724dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
2725dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
2726dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
2727dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
2728dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
2729dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2730dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2731dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2732dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
2733dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
2734dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
2735dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
2736433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
2737dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2738285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
27392b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
2740285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
2741f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
2742f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
2743f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
2744f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
2745f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
274678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.resetNameVerifiedForOtherRawContacts(rawContactId);
2747f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
2748f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
2749f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
275019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
2751d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mTransactionContext.rawContactInserted(rawContactId,
2752d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        new Account(accountName, accountType));
275319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
27545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
27555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
275633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
275733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
2758321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
2759f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
276020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
276120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
276220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
27635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
276420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
276520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
276620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
276720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
276820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
2769b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
277020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
277120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
277297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
277397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
277497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
277597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
277697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
2777653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
277820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2779653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2780653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
2781f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(uri, DataRowHandler.DataUpdateQuery.COLUMNS,
2782f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2783653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
2784653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
2785f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
278620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2787653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
2788653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
278920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
279020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2791653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
279220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
279320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2794f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
2795653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
2796653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
2797321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
2798653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
2799f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
2800a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2801d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (rowHandler.update(mDb, mTransactionContext, values, c, callerIsSyncAdapter)) {
2802813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
2803813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
2804813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
2805a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2806321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
2807321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
28088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
2809dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
28108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
2811b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
28128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
28138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
28148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
28158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
28168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
2817dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
28188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
28198c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
28208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
28218c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
28228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
28238c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
28248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
28258c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
28268c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
2827dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
2828dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
2829d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
28308c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
2831b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
2832d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
2833b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
2834d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
2835b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
2836d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
2837b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
2838d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
2839b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
2840d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
2841d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2842d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
28438c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
2844d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
2845d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
2846d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
28478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
2848c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
28498c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
2850c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
2851c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
28524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
285397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
285497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
28558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
2856dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
2857dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
2858dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
2859dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
2860dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
2861dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
2862dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
2863dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
2864dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
2865dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2866dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
2867dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
2868dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2869dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2870dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
28718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
28728c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
28738c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
2874b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
28758c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
2876b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
28778c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
2878b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
28798c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
2880b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
28818c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
2882b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
28838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
28848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
28859b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
28866e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
28879b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
28889b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
28899b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
28909b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
28919b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
28929b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
2893f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2894d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2895127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
2896127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
28970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
28980c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
289980c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
2900ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
2901ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
29020c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
29030c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
29040c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
29050c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
29060c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
29070c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
2908b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
2909127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
29100c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
29114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
29124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
29130c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
29144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
29154da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
29160c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
29176bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
29186bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
29190c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
29200c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
29210c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
29220c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
2923127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
2924127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
29253389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
292669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
292769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
292869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
292969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
2930dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
29310dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1);
29320dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2);
2933127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
2934127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
2935127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
2936127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
2937b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
2938b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
293970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
2940bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
29413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
29423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2943bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
2944f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
2945e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
2946627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
294749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
294870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
294970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
2950dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
2951743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
2952743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
2953743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
2954743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
2955e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
2956743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
2957743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
2958743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
2959743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
2960743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
296148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
2962627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
2963743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
2964627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
2965627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
2966627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
296770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
296870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
296933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
2970e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
2971e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
2972e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
2973e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
2974e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
2975e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
2976e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
2977e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
2978e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
2979e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
2980e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
2981e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
2982e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
2983e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
2984e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
2985e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
2986e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
2987e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
2988e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
2989e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
2990e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
2991e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
2992e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
2993e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
2994e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
2995e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
2996e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
2997d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
2998d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
2999d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
3000d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
30014458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
3002e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
3003e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
300433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
300533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
3006e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
300733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
300833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
300933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
301033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
301169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
301269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
301369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
301433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
301533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
301669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
301769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
301833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
301933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
302033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
302133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
302233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
302333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
302433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
302533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
302633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
302733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
302833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
3029e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
303033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
303133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
3032e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
3033e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
3034e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
303570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
303670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
303770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
303870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
303973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
30403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
30413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
30423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
30433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
30443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
30453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3046afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
304770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3048619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
30493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
30503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
30513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
30523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
30533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
30543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
30553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
30563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
30573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
30583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
30593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
30603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
30613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
30623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
30633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
30643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
30653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
30663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
30673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
30683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
306972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
3070bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
3071d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3072d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3073619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3074627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
3075627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
3076dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
3077743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
3078743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
3079743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
3080627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
3081627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
3082dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
3083627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
3084627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3085627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3086627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
3087627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
3088627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
3089627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
3090627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
30916ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private static class DirectoryCursorWrapper extends CursorWrapper
30926ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            implements CrossProcessCursor {
30936ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        private final CrossProcessCursor mCrossProcessCursor;
30946ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
30956ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        public DirectoryCursorWrapper(Cursor cursor, CrossProcessCursor crossProcessCursor) {
30966ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            super(cursor);
30976ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            mCrossProcessCursor = crossProcessCursor;
30986ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
30996ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31006ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        @Override
31016ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        public void fillWindow(int pos, CursorWindow window) {
31026ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            mCrossProcessCursor.fillWindow(pos, window);
31036ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31046ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31056ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        @Override
31066ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        public CursorWindow getWindow() {
31076ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return mCrossProcessCursor.getWindow();
31086ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31096ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31106ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        @Override
31116ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        public boolean onMove(int oldPosition, int newPosition) {
31126ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return mCrossProcessCursor.onMove(oldPosition, newPosition);
31136ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31146ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
31156ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
31174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
31184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
311915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
312015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
312115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
3122d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
3123385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
3124385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1);
3125385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
3126385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder,
3127385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                    Directory.DEFAULT);
3128d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
3129385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder,
3130385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                    Directory.LOCAL_INVISIBLE);
3131d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3132d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3133d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
3134d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
3135a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
3136a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
3137d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3138d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3139d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
3140d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
3141d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
3142d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
3143d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
3144d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
3145d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3146d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
3147d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
3148d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
31492e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
31502e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
31512e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
31522e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
31532e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
31542e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
3155d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
315609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
315709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
315809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
315909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
316009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
3161332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
3162d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
31636ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31646ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
31656ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
31666ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31676ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31686ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
31696ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (crossProcessCursor != null) {
31706ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return new DirectoryCursorWrapper(cursor, crossProcessCursor);
31716ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
31726ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return matrixCursorFromCursor(cursor);
31736ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31746ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
31756ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31766ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
31776ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
31786ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
31796ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
31806ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
31816ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
31826ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
31836ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
31846ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31856ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
31866ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31876ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
31886ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
31896ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
31906ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
31916ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
31926ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
31936ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
31946ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
31956ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
31966ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
3197332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
31986ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
3199d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3200d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3201d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
3202d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
3203d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
3204d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
3205d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
3206d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
3207d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
3208d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3209d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
3210d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
3211d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
3212d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
3213d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3214d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3215d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
3216d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
3217d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
3218d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
32194458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
32204458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
32214458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
322249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
322349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
32244458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
32254458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
32264458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
32274458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
32284458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
32294458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
32304458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
32314458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
32324458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
32334458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
32344458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
32354458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
32364458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
3237d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
32384458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
3239d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3240d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
32414458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
32424458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
3243d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3244d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
324572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
32464458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
32474458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
32484458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
324972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
325072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
3251d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    public Cursor queryLocal(Uri uri, String[] projection, String selection, String[] selectionArgs,
3252385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                String sortOrder, long directoryId) {
3253bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3254bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
3255bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
32560b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
3257b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
325835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3259d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
32601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
3261c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
3262c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3263619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
3264619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
3265a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
32664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
326735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3268b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
326935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
327035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3271d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3272763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3273385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
3274619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
3275619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
3276619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3277d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
32784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3279763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
32804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
32814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
32826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
32836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
32846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
32855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
32865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
32875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
32885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
32895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
3290fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3291fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
32925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
3293a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
32945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
32955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
32965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
32975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3298763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
3299a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3300a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3301a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3302a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
3303a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
33045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
33055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
33065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
33075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3308763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
33094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
33104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
33114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
33125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
33135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
33145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
33152149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
33162149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
33172149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
33182149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
33192149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
33202149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
33212149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
33222149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
33232149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
33242149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
33252149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
33262149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
33272149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
3328a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
3329a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3330a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3331a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
3332a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
33332149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
33342149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
33352149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
33362149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
33372149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
33382149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
33392149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
33402149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
33412149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
33422149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
33432149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
33442149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
33452149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
3346f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
3347f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
334842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
3349763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
3350f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
33514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
33524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
33534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
3354f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
3355f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
3356f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
335742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
335842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
335942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
336042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
336142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
336242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
336342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
336442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
336542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
336642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
3367ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
3368916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
3369ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
3370916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
3371ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
3372916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(qb, uri, projection, filterParam);
3373385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
3374ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3375ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3376ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3377ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
3378ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
33794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3380ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3381d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
33824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
33834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3384e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
33855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
33864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
33874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
33884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3389763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3390ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
33915e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
33925e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
33935e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
3394dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    starredProjection =
3395dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
3396dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    frequentProjection =
3397dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
33985e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
33995e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
34004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
34014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
34024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3403d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
34045e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
34055e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
34064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3407d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3408d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3409d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3410763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
34114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
34124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3413d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
34145e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
34155e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
3416d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3417d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
34184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3419d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3420d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3421d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3422d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
34234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
34244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3425d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3426d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3427d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3428d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3429d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3430d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3431ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3432763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3433b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
343471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
34354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3436b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3437b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3438b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3439b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
3440a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
34414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
344282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
34434da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
34444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
34456bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
34466bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
344700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3448a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
34493653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
345082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
34514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
34524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
34533653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
34543653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
34553653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
34563653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
3457a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
3458a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
3459a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
3460a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
3461a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
3462a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
3463a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3464a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3465a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
3466a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
3467a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
3468a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
3469a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
3470a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3471a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
3472a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
3473a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
3474a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
3475a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
3476a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3477a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
3478a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
3479a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3480a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3481a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3482a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
3483a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
3484a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
3485a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
3486a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
3487a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
3488a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3489a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
3490a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
3491a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
3492a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
3493a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
3494a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3495a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
34964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
349782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
349889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
34992815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
35002815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
35012815f58f72f109790585931f601a63ddc02536a5Evan Millar
350248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
350382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
35044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
350548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
35064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
350748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
350848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
350948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3510ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
351182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
351289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3513ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
35144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
35154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3516a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
35175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
351845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
35195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
35205e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
35215e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
35225e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
35237318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
35245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
352545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
35265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
35275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3528892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
3529892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
35305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
35315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
35325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
35335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
3534892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
3535892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
3536892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
3537892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
3538892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
353945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
354045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
354145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
354245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
354345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
354445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
354545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
35465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
35475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3548a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
3549ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
35505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3551a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3552a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3553a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
3554ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3555ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3556ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
35574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
355882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
355989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
35604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
35614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
35624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
356348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
356482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
35654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
35664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
35674da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
356848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
356948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
357048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
35715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
357282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
357389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
35744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
357508768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
357608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
357708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
357808768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
35794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3580ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3581ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3582ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
35835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
358482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
358507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
358607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
358707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
358807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
358907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
359007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
359107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
35925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
359307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
359407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
359507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
359607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
359707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
359807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
359907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
360007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
360107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
36022a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
36032a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(mDbHelper.getMimeTypeIdForEmail());
36042a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
360507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
360620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
360720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
360820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
360907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
361007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            /*
361107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * Using a UNION instead of an "OR" to make SQLite use the right
361207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * indexes. We need it to use the (mimetype,data1) index for the
361307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * email lookup (see above), but not for the name lookup.
361407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * SQLite is not smart enough to use the index on one side of an OR
361507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * but not on the other. Using two separate nested queries
361607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * and a UNION between them does the job.
361707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             */
361807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            sb.append(
361907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " UNION SELECT " + Data._ID +
362007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " FROM " + Tables.DATA +
36212a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                                    " WHERE +" + DataColumns.MIMETYPE_ID + "=");
36222a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            sb.append(mDbHelper.getMimeTypeIdForEmail());
36232a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            sb.append(" AND " + Data.RAW_CONTACT_ID + " IN ");
36247318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
362520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
36265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
36275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3628a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
36295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
36305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
3631a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3632c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov                    sortOrder = EMAIL_FILTER_SORT_ORDER;
3633a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
36345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
36355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
36365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3637ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
363882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
363989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
364089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
3641ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3642ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3643ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
364448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
364582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
364748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
364848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
36494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
365048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
365148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
365248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
36535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
3654763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
36554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
36564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
36574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36585ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
36595ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
3660763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
36614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
36624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
36634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
36644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
36654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
36675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
366882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36694da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
36704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
3671e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3672e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3673e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
3674e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
367582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3676e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3677e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3678e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
36794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
368082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
36824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
3683a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
3684a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
3685a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
3686a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
36874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3688a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
3689a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
3690a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
3691892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
3692a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
3693a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3694e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
3695e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
3696e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        mDbHelper.getCurrentCountryIso());
3697892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
3698892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
3699892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
3700e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
3701e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
3702e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
3703e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
3704a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
3705a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
3706a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3707ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
3708b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3709ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
371089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3711ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3712ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3713ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3714ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3715b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3716ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
37174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
37184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
3719ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3720ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3721ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3722ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
3723b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
3724ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
372589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
372689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
3727ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3728ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3729ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3730b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
37310c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
3732b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
3733b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3734b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3735b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
373631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
3737d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
37382d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
37392d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
37402d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
37412d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
374231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
3743d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
3744d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
374531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
374631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
374731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
374831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
37495b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
37505b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
37515b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
37525b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
37535b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
37545b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
37555b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
37565b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
375776dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
37585b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
37595b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
37605b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
37615b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
37625b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
37635b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
37645b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
3765763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
37667581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
37677581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
37685b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
376931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
377031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3771eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3772eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
3773eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
377489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3775e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3776e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
3777e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
3778b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
3779e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
378082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3781b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
3782e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3783e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
378482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3785b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
3786e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3787e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3788e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3789eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3790eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3791eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
379282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
37930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
37945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
37955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
37965ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
379782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
37980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
37994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
38004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
38015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
38025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
38035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
3804c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
3805a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
3806c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3807c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3808c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
38092d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
38102d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, lookupKey, projection);
3811c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3812c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
38131b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
3814b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
38151b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
38161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
38171b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
38181b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
3819b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
38201b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
38211b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
38221b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
38231b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
38241b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
3825b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
38261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
38271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
38281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
38291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
38301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
3831b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
38321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
383371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
38341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
38351b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
38361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
383746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
3838a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
383946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
384046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
384146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
384246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
384346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
3844a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
38454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
38464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
384746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
384846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
384946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
385009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
385109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
385209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
385309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
3854d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
3855d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
3856d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
3857d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3858d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3859d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3860d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
3861385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
3862d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
3863d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
3864385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
3865d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
3866d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3867d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3868d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
38697a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
38707a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
38717a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
38727a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
38734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
3874f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
3875c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
38764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
38774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
38787f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov        qb.setStrictProjectionMap(true);
38797f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
3880ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
3881ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
3882ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
3883ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
3884ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
3885ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
38865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
38875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
38895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
38905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
3891038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
3892038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
3893038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
3894038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
38955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
38965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
38974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
38984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
38994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
39004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
39014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
39024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
390309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
390409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
390509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
390609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
390709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
390809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
390909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
391009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
391109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
391209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
391309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
391409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
391509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
391609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
391709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
391809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
3919a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
3920a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
3921a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
3922a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
3923a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
3924a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
3925a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
3926a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
3927a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
3928a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
3929a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
3930a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
3931a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
3932a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
3933a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
3934a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
3935a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
3936a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
3937a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
3938a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
3939a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
3940a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
3941a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
3942a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
3943a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
3944a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3945a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
3946a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
3947a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
394809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
3949bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
3950bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
3951bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
3952bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
3953ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3954bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
3955bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
3956ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
3957ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3958bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
3959bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
3960bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
3961bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
3962de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
3963ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
3964ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3965ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
3966ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
3967ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
3968ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
3969ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
3970ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
3971ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
3972ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3973ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
3974ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
3975ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
3976ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
3977ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
3978ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
3979ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
3980ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
3981ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
3982ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
3983ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
3984ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
3985ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
3986ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
3987ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
3988ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3989bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
3990ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
3991bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
3992bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "SUBSTR(" + sortKey + ",1,1) AS " + AddressBookIndexQuery.LETTER);
3993bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
3994bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
3995bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
3996bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
3997bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
3998bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
3999bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
4000bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
4001ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
4002bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "GET_PHONEBOOK_INDEX(SUBSTR(" + sortKey + ",1,1),'" + locale + "')"
4003bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
4004ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
4005ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
4006ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
4007ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4008f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
4009ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
4010ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
4011ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4012ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
4013f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
4014ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
4015ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
4016bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
4017bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
4018bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4019bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
4020bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
4021bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
4022ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
4023f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
4024bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
4025bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
4026bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
4027bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
4028bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
4029bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
4030bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
4031bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
4032bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
4033bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
4034bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4035bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
4036bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
4037bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
4038bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
4039bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4040bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
4041bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
4042bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
4043ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4044ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4045e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return new AddressBookCursor((CrossProcessCursor) cursor, titles, counts);
4046ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
4047f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
4048ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4049ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4050ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
40512d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
405292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
405392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
405492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
405592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
40562d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
40572d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
40585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
40595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
40605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
406192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
406292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
406392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
406492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
406592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
406692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
406792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
406892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
406992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
407092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
407192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
407292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
407392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
407492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
407592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
407692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
407792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
407892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
407992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
40805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
40815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
40845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
40875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
40885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
40905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
40915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
40925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
40935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
40945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
40955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
40975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
40985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
40995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
41005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
41035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
41045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
41055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
41065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
41075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
410892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
41095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
41105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
41115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
41145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
41155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
41175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
41185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
41195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
41205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
41215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
41225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
41235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
41245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
41255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
41265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
412792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
412892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
41295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
41305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
41315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
41325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
41335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
41345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
41365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
41375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
41405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
414292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
414392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
41445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
41465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
41475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
41485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
414992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
41505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
41515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
41535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
41545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
415592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
41565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
415892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
415992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
416092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
416192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
41625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
41635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
416492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
416592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
416692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
41675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
416992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
417092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
41715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
417292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
417392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
417492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
417592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
417692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
417792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
417892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
417992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
418092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
418192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
418292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
418392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
418492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
418592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
418692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
418792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
418892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
418992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
419092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
419192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
419292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
41935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
419592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
419692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
419792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
419892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
419992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
420092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
420192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
420292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
420392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
420492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
420592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
420692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
420792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
420892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
420992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
421092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
421192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
421292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
421392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
421492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
421592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
42165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
42175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
42185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
42195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
422092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
422192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
42225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
42235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
42245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
42255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
42275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
42285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
42295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
42315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
42325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
42335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
42345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
42355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
42365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
42375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
42385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
42395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
42405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
424192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
424292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
424392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
42445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
42455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
42465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
42475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
42485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
42495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
42505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
42515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
42525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
42555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
42565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
425792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
425892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
425992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
426092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
426192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
426292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
426392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
426492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
426592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
426692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
426792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
4268ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
4269ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
4270ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
4271ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
42725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
42735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
42745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
42755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
42765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
42775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
42795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
42805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
42825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
42835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
42855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
42865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
42875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
42885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
42895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
42905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
42915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
42925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
42935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
42945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
42955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
42965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
42975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
42985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
42995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
43005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
43015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
43025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
43035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
43045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
43055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
43065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4307763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
4308763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
430982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4310916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
4311916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
4312916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
4313916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
4314916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4315916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
4316916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
4317916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
4318916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
4319916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
4320916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            String[] projection, String filter) {
4321916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4322916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
4323916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
4324916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4325916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(" JOIN (SELECT " +
4326916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                RawContacts.CONTACT_ID + " AS snippet_contact_id");
4327916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4328916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA_ID)) {
4329916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", " + DataColumns.CONCRETE_ID + " AS "
4330916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    + SearchSnippetColumns.SNIPPET_DATA_ID);
4331916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
4332916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
43339c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA1)) {
43349c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA1 + " AS " + SearchSnippetColumns.SNIPPET_DATA1);
4335916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
4336916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
43379c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA2)) {
43389c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA2 + " AS " + SearchSnippetColumns.SNIPPET_DATA2);
4339916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
4340916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
43419c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA3)) {
43429c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA3 + " AS " + SearchSnippetColumns.SNIPPET_DATA3);
43439c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
43449c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov
43459c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA4)) {
43469c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA4 + " AS " + SearchSnippetColumns.SNIPPET_DATA4);
4347916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
4348916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4349916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_MIMETYPE)) {
4350916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", (" +
4351916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    "SELECT " + MimetypesColumns.MIMETYPE +
4352916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " FROM " + Tables.MIMETYPES +
4353916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " WHERE " + MimetypesColumns._ID + "=" + DataColumns.MIMETYPE_ID +
4354916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    ") AS " + SearchSnippetColumns.SNIPPET_MIMETYPE);
4355916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
4356916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4357c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS + " WHERE ");
4358c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
43599a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        appendGeneralDataFilter(sb, filter);
4360a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
4361a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
4362a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
4363a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov        qb.setTables(sb.toString());
4364a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
4365a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
4366a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
4367916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void appendContactsTables(StringBuilder sb, Uri uri, String[] projection) {
4368763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4369f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4370763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4371763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4372d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4373763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4374763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
4375a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
4376a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
437782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
4378ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
4379763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
4380763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
4381763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4382f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4383763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4384763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4385d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4386763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4387763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
4388763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
4389763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
4390763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
4391763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
4392763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4393a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
4394a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(mDbHelper.getRawEntitiesView(shouldExcludeRestrictedData(uri)));
4395a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
439646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
439746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
439846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
439982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
440082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
440182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4402a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getDataView(shouldExcludeRestrictedData(uri)));
440382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
440482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
4405a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
4406a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
4407a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
4408a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
44093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
441082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
4411f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
4412f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
4413f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
4414f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
4415f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
441682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
4417ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
4418ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
44190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
44200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
44210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4422b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
44230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
4424a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
4425a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
44260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4427a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
4428a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
4429a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
4430a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4431a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
4432a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
4433a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
4434a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getEntitiesView(shouldExcludeRestrictedData(uri)));
4435a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
4436a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4437a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
4438a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
4439a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
4440a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
4441a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4442a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
4443a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
4444a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
4445a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
4446a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4447a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
4448a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
4449a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
4450a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
4451a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
4452a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
4453a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
4454a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
4455a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
4456a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
4457a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
4458a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
44590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
4460a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
44610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4462a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
4463a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
4464b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
44650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
44660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
44670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
44680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
44690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
44700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
4471a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
4472a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
44730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
4474a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
4475a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4476a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
4477a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
4478a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
4479a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
4480a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
4481a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
4482a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
4483a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4484a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
4485a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4486a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
4487a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
4488a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
4489a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
4490a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
4491a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4492a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
4493a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4494385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    private void appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
4495385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
4496385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
4497385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
4498385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
4499385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
4500385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
4501385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
4502a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private boolean shouldExcludeRestrictedData(Uri uri) {
4503a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        // Note: currently, "export only" equals to "restricted", but may not in the future.
4504a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
4505a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.FOR_EXPORT_ONLY, false);
4506a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (excludeRestrictedData) {
4507a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return true;
4508a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4509a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4510a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4511a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4512a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (requestingPackage != null) {
4513a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4514a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4515a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4516a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return false;
45170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
45180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
45194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
4520f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
4521f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
4522e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
4523e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
4524e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
4525e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
4526fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4527fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
4528e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
4529e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
4530e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
4531e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
4532e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
4533e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
45344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
45354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
45364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
45374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
45384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
45394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
45404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
45414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
45424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4543e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
4544f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
4545f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
4546e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
4547e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
4548e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
4549e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
4550fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4551fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
4552e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
4553e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
4554e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
4555e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
4556e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
4557e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
4558e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
4559e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4560e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
4561e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
4562e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
4563e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
4564e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
4565e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
4566e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
4567e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
4568e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
4569e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
4570e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
4571e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
4572e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
45737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
4574c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
4575c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
4576c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
4577c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
4578c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
4579f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
45802e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
4581c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
4582c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4583c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4584c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
4585c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
4586c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
4587c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
4588c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
4589c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
4590c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4591c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
4592c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
4593c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
4594c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4595c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4596c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4597c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
459800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
4599d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
460070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
460170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
4602fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
460370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
460470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
460570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
460670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
4607d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
460870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
460967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
46105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
46115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
4612619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
4613619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
4614619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4615b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
4616f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
4617415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
4618415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
4619415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
4620b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
4621b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
4622a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
4623f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
4624e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " + RawContacts.CONTACT_ID + "=?",
4625e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
4626e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
4627b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4628e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
4629f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
4630e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
46314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
4632d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4633d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4634f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
463549d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
463642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
463749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(lookupContactIdByLookupKey(db, lookupKey));
463842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + "=?";
463942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
464042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
464142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
464242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
464342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
464442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                outputRawContactsAsVCard(localStream, selection, mSelectionArgs1);
4645f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
464642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
464742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
464842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
464949d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
465042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
465142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
465242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
465342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
4654d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
4655d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
465642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
465742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
4658d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
465942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
4660d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
466142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
466249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                    inBuilder.append(lookupContactIdByLookupKey(db, lookupKey));
466342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
466442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
466542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
466642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
4667d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4668d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
4669d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
4670d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
4671d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
4672d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                outputRawContactsAsVCard(localStream, selection, null);
4673f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
4674d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4675b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4676b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
4677fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
4678fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
4679b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
4680b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
4681b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4682f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,
4683e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
4684e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
4685e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
4686e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
4687e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
4688e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
4689e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
4690e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
4691e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
4692e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
4693e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
469408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
4695f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
4696f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
469708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
469808ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
469908ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
470008ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
4701e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
4702e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
4703d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
4704d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4705d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4706f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
4707d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
4708d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4709f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
4710d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
4711d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
4712d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4713d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
4714d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4715f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
4716f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
4717f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
4718d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
4719ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
4720ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
4721d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4722d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4723d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4724f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
4725f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
4726f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
4727f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
4728f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
4729f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
4730f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
4731f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
4732d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4733d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
4734d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
4735d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
4736d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4737d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
4738d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
4739d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
47407a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
47417a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa                new VCardComposer(context, VCardConfig.VCARD_TYPE_DEFAULT, false);
4742d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
4743d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4744f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
47457a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        if (!composer.init(selection, selectionArgs)) {
47467a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa            Log.w(TAG, "Failed to init VCardComposer");
4747d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
47487a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        }
4749d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4750d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
4751d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
4752d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
4753d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4754d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4755d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
4756d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4757b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
47584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
47594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
4760415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
4761415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
4762415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
4763a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
47644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
4765b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
4766be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
47672d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
4768b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
4769b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
4770b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
4771f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
477242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
4773f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
4774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
4775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                return "image/png";
4776b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
4777be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
4778b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
4779b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
4780f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
4781f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
4782508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
4783b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
478448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
478548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
478648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
478748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
47889005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
47899005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
479048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
479148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
479248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
479348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
479448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
479548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
479648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
479748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
4798b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
4799b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
4800b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
4801b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
4802b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
4803b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
4804b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
4805b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
4806c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
4807c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
4808c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
4809c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
4810d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
4811d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
4812d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
4813d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
481461efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
481561efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
48164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
48174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
48187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
481909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
482009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
482109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
482209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
482309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
482409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
482509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
482609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
482709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
482809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
48298727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
48308727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
48318727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
483209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
483309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
483409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
483509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
483609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
483709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
483809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
483909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
484009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
484109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
484209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
484309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
484409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
484509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
484609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
484709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
484809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
484909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
485009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
485109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
485209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
485309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
485409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
485509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
485609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
485709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
485809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
485909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
486009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
486109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
486209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
486309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
486409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
486509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
486609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
486709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4868f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
4869f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4870f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
4871f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
4872f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4873f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4874f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4875f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
4876f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
487778fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.insertNameLookup(rawContactId, dataId, lookupType, name);
4878f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4879f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4880f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4881f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
4882d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
4883f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4884f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4885f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
48862d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
4887d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
4888d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
4889d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
4890d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
4891d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
4892d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
4893d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
4894e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
4895916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
4896916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
4897e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
4898e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
48995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
4900c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
49017318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam);
4902c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
4903c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4904c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
49057318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam) {
49067318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), true);
49075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
49085e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
49095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
49107318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov            boolean allowEmailMatch) {
491123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        if (TextUtils.isEmpty(normalizedName)) {
491223061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // Effectively an empty IN clause - SQL syntax does not allow an actual empty list here
491323061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("(0)");
491423061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        } else {
491523061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("(" +
491623061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
491723061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " FROM " + Tables.NAME_LOOKUP +
491823061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " WHERE " + NameLookupColumns.NORMALIZED_NAME +
491923061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " GLOB '");
492023061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // Should not use a "?" argument placeholder here, because
492123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME.
492223061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append(normalizedName);
492323061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
492423061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_COLLATION_KEY + ","
492523061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NICKNAME + ","
492623061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_SHORTHAND + ","
492723061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.ORGANIZATION + ","
492823061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_CONSONANTS);
492923061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            if (allowEmailMatch) {
493023061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
493123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            }
493223061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("))");
493323061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        }
4934ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
4935ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
49369a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private void appendGeneralDataFilter(StringBuilder sb, String filter) {
49379a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (filter != null) {
49389a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            filter = filter.trim();
49399a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
49409a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
49419a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (TextUtils.isEmpty(filter)) {
49429a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append("0");     // Empty filter - return an empty set
49439a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return;
49449a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
49459a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
49469a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (filter.indexOf('@') != -1) {
49479a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            String address = mDbHelper.extractAddressFromEmailAddress(filter);
49489a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (!TextUtils.isEmpty(address)) {
49499a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append(DataColumns.CONCRETE_ID + " IN (" +
49509a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                        "SELECT MIN(" + DataColumns.CONCRETE_ID + ")" +
49519a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                        " FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
49529a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                        " WHERE " + DataColumns.MIMETYPE_ID + " IN (");
49539a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append(mDbHelper.getMimeTypeIdForEmail());
49549a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append(",");
49559a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append(mDbHelper.getMimeTypeIdForIm());
49569a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append(",");
49579a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append(mDbHelper.getMimeTypeIdForSip());
49589a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append(") AND " + Data.DATA1 + " LIKE(");
49599a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, address + '%');
49609a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append(")" +
49619a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                        " GROUP BY " + RawContactsColumns.CONCRETE_CONTACT_ID +
49629a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                        ")");
49639a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return;
49649a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
49659a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
49669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
49679a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String normalizedFilter = NameNormalizer.normalize(filter);
49689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!TextUtils.isEmpty(normalizedFilter)) {
49699a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append(DataColumns.CONCRETE_ID + " IN (");
49709a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
49719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            // Construct a query that gives us exactly one data _id per matching contact.
49729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            // MIN stands in for ANY in this context.
49739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append(
49749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    "SELECT MIN(" + Tables.NAME_LOOKUP + "." + NameLookupColumns.DATA_ID + ")" +
49759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    " FROM " + Tables.NAME_LOOKUP +
49769a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    " JOIN " + Tables.RAW_CONTACTS +
49779a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    " ON (" + RawContactsColumns.CONCRETE_ID
49789a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                            + "=" + Tables.NAME_LOOKUP + "."
49799a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                                    + NameLookupColumns.RAW_CONTACT_ID + ")" +
49809a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    " WHERE " + NameLookupColumns.NORMALIZED_NAME + " GLOB '");
49819a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append(normalizedFilter);
49829a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
49839a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                        " IN(" + CONTACT_LOOKUP_NAME_TYPES + ")" +
49849a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    " GROUP BY " + RawContactsColumns.CONCRETE_CONTACT_ID +
49859a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    ")");
49869a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        } else {
49879a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append("0");     // Empty filter - return an empty set
49889a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
49899a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
49909a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (isPhoneNumber(filter)) {
49919a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            String number = PhoneNumberUtils.normalizeNumber(filter);
49929a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append(" OR " + DataColumns.CONCRETE_ID + " IN (" +
49939a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    " SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
49949a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    + " FROM " + Tables.PHONE_LOOKUP
49959a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
49969a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append(number);
49979a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append("%'");
49989a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
49999a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
50009a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    mDbHelper.getCountryIso());
50019a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (!TextUtils.isEmpty(numberE164)) {
50029a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
50039a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append(numberE164);
50049a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                sb.append("%'");
50059a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
50069a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append(')');
50079a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50089a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
50099a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50109a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean appendEmailBasedDataFilter(StringBuilder sb, String filter) {
50119a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (filter.indexOf('@') == -1) {
50129a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return false;
50139a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50149a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50159a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String address = mDbHelper.extractAddressFromEmailAddress(filter);
50169a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (TextUtils.isEmpty(address)) {
50179a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return false;
50189a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50199a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50209a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(DataColumns.MIMETYPE_ID + " IN (");
50219a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(mDbHelper.getMimeTypeIdForEmail());
50229a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(",");
50239a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(mDbHelper.getMimeTypeIdForIm());
50249a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(",");
50259a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(mDbHelper.getMimeTypeIdForSip());
50269a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(") AND " + Data.DATA1 + " LIKE(");
50279a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, address + '%');
50289a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(")");
50299a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return true;
50309a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
50319a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50329a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean appendPhoneNumberBasedDataFilter(StringBuilder sb, String filter) {
50339a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!isPhoneNumber(filter)) {
50349a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return false;
50359a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50369a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50379a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String number = PhoneNumberUtils.normalizeNumber(filter);
50389a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(DataColumns.CONCRETE_ID + " IN " +
50399a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                "(SELECT " + PhoneLookupColumns.DATA_ID
50409a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                + " FROM " + Tables.PHONE_LOOKUP
50419a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
50429a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(number);
50439a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append("%'");
50449a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50459a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String numberE164 = PhoneNumberUtils.formatNumberToE164(number, mDbHelper.getCountryIso());
50469a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!TextUtils.isEmpty(numberE164)) {
50479a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
50489a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append(numberE164);
50499a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append("%'");
50509a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50519a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(")");
50529a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50539a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String normalizedFilter = NameNormalizer.normalize(filter);
50549a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (TextUtils.isEmpty(normalizedFilter)) {
50559a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return true;
50569a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50579a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50589a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(" OR " + DataColumns.CONCRETE_RAW_CONTACT_ID + " IN " +
50599a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                "(SELECT " + NameLookupColumns.RAW_CONTACT_ID +
50609a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
50619a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
50629a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " GLOB '");
50639a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(normalizedFilter);
50649a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
50659a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                + CONTACT_LOOKUP_NAME_TYPES + "))");
50669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return true;
50679a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
50689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50699a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean appendNameBasedRawContactFilter(StringBuilder sb, String filter) {
50709a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String normalizedFilter = NameNormalizer.normalize(filter);
50719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (TextUtils.isEmpty(normalizedFilter)) {
50729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return false;
50739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(DataColumns.CONCRETE_RAW_CONTACT_ID + " IN " +
50769a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                "(SELECT " + NameLookupColumns.RAW_CONTACT_ID +
50779a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
50789a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
50799a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " GLOB '");
50809a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        // Should not use a "?" argument placeholder here, because
50819a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME.
50829a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(normalizedFilter);
50839a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
50849a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                + CONTACT_LOOKUP_NAME_TYPES + "))");
50859a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return true;
50869a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
50879a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50889a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
50899a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
50909a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
50919a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
50929a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
50939a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
50949a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
50959a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
50969a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
50979a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
50989a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
50999a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
51009a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
51019a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
51029a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
51034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
51047a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
51057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
51067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
51077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
51087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
51097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
51107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
51117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
51127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51137a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
5114f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
5115f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
51167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
51187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
51197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
51207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
51217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
51227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
51237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
51247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
51257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
51267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
51277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
51287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
51297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
51317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
51337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
51347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
51357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
51367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
51377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
51387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
51397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
51407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
51427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
51437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
51447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
51457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
51467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
51477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
51487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
51497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
51524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
51534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
51544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5155b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
5156b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
5157b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
5158b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
5159b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
51604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
51614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5162b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
5163b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5164b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5165caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
51665e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
51675e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
51685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
51695e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
51705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
51715e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
51725e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
51735e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
51745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
51755e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
51765e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5177caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
5178caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
5179caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
51805f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
5181caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
5182caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
5183caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
5184caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
51856f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
5186caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
51876f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
5188caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
5189f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
519073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
519173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
519273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
519373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
5194bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
5195bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
5196bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
5197bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
519873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
519973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
520073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
520173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
520273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
5203627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
5204627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
5205627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
5206627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
520773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
520873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
520973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
5210627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
5211627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
5212627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
5213627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
5214627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
521573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
521673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
521773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
521873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
521973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
522073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
522173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
5222627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
5223b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
5224d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5225f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
5226f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
5227f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5228f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
5229f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5230f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5231f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5232f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5233f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5234f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
5235f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
5236f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5237f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5238f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5239f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
5240f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5241f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
5242f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
5243f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5244f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5245f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
5246f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
5247f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
5248f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
5249f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
5250f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5251f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5252f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
5253f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
5254f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
5255f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
5256f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5257f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5258f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
5259f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5260f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5261f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
5262f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
5263f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5264f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
5265f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
5266f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
5267f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
5268f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
5269f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5270f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5271f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5272f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
5273f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5274f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
5275f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5276f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5277f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5278f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
5279f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
5280f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
5281f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5282f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5283f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5284f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
5285f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
5286f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
5287f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
5288f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
5289f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5290f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5291f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
5292f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
52935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
52940dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
52950dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
52960dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
52970dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
52980dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
52990dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
53000dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
53010dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
53020dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
5303bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
53040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
53050dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
53060dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53070dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
53080dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
53090dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
53100dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
531149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = mDbHelper.getWritableDatabase();
53120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
53130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
53140dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
53150dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
53160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
53170dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
53180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
53190dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE,
53200dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
53210dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
53220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
53230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
53240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
53250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
53260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
53270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
53280dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
53290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
53300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
53310dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mContactAggregator.aggregateInTransaction(mDb);
53320dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
53330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
53340dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
53350dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
53360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
53370dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
53380dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
53390dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
53400dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
53410dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
53429a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
53439a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
53449a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
53459a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
53469a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
53479a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
53489a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
53499a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
53509a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
53514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
5352