ContactsProvider2.java revision 03197a00e17386aa9b1971bde3cda034bc17e0c3
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.NameLookupColumns;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
3603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
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;
15605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
15705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
15805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
15905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
160619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
165b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1673d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
169b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
170b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
17151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
1723d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1730dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
1740dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
1750dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
1760e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1770e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
178a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
180dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov    private static final String TIMES_CONTACTED_SORT_COLUMN = "times_contacted_sort";
1815e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
182d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
183dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov            + TIMES_CONTACTED_SORT_COLUMN + " DESC, "
1849b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
185d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
186d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
187d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
188d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1896e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
1909b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
1919b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1929b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
1939b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
1946e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
1959b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
1969b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1979b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
1989b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
199de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
200de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
201d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
202d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
205a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
210a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
211f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
21242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private static final int CONTACTS_AS_MULTI_VCARD = 1011;
2132149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_DATA = 1012;
2142149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_DATA = 1013;
215a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_ENTITIES = 1014;
216a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ENTITIES = 1015;
217a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1016;
2184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
22246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
2234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2246bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
226ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
22748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
22848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
22948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
23048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
23148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
23248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
23348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
23448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
235a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2366bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2376bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
238b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
239b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
240b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
24182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
24282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
24431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
24531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
246eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
247eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
248ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
249ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
250ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
251ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
25235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
253b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
25435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
255c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
256c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
257c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
26346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
26446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
26509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
26609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
267d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
268d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
269d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
2707a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
2717a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
272dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
273dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
274dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
275dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
276dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
277dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE
278dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
279dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
280dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
281dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
282dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
283dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
284dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
285dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND "
286dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + Groups.AUTO_ADD + " != 0";
287dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
288dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
289dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
290dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
291dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
292dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
293dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
294dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
295dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
296dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
297dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
298dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
299e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    public class AddressBookCursor extends CursorWrapper implements CrossProcessCursor {
300e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final CrossProcessCursor mCursor;
301e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final Bundle mBundle;
302e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
303e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public AddressBookCursor(CrossProcessCursor cursor, String[] titles, int[] counts) {
304e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            super(cursor);
305e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor = cursor;
306e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle = new Bundle();
307e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
308e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
309e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
310e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
311e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
312e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public Bundle getExtras() {
313e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mBundle;
314e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
315e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
316e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
317e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public void fillWindow(int pos, CursorWindow window) {
318e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor.fillWindow(pos, window);
319e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
320e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
321e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
322e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public CursorWindow getWindow() {
323e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.getWindow();
324e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
325e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
326e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
327e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public boolean onMove(int oldPosition, int newPosition) {
328e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.onMove(oldPosition, newPosition);
329e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
330e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    }
331e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
332d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
333f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
334f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
335f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
33667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
33767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
3386cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
3393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
340f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
341ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
342ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
343d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
34467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
345d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
346ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
348f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
34919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
35019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
35119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
352ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
353ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
354ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
35519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
35619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
35719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
358ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
359ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
36019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
36119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
362c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
363caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
36471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
36571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
36671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
36771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
36871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
36971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
37071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
37171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
37271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
37371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
37471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
37571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
37671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
37771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
378a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
379a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
380a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
381a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
382a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
383a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
384a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
385a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
386a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
387a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
388a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
389a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
390c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
391c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
392c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
393c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
394c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
395c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
396c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String TIME_SINCE_LAST_CONTACTED =
397c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            "(strftime('%s', 'now') - " + Contacts.LAST_TIME_CONTACTED + "/1000)";
398c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
399c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
400c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
401c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Within the starred/unstarred groups - three buckets: very recently contacted, then fairly
402c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
403c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * of times contacted. If all else fails, alphabetical.  (Super)primary email
404c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * address is returned before other addresses for the same contact.
405c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
406c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
407c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            "(CASE WHEN " + Contacts.STARRED + "=1 THEN 0 ELSE 1 END), "
408c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + "(CASE WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_CURRENT + " THEN 0 "
409c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + " WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_RECENT + " THEN 1 "
410c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + " ELSE 2 END),"
411c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Contacts.TIMES_CONTACTED + " DESC, "
412c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Contacts.DISPLAY_NAME + ", "
413c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Data.CONTACT_ID + ", "
414c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Data.IS_SUPER_PRIMARY + " DESC";
415c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
416916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
417916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
418916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
419916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
420916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NICKNAME + "," +
421916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_SHORTHAND + "," +
422f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.ORGANIZATION + "," +
423f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.NAME_CONSONANTS;
424916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
425f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
426f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
427f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
428f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
429f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
430f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
431f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
432f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
433f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
434f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
435f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
436f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
437f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
438f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
439f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
440916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
441f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
442f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
443f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
444f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
445f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
446f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
447f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
448f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
449f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
450f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
451f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
4523d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
4533d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
454f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
455f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
456f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
457f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
458f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
459cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
460f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
461f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
462f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
463f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
464f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
465f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
466f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
467f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
468f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
469f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
470f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
471f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
472f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
473f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
474f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
475f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
476f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
477f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
478f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
479f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
48003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
481f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
482f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
483f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
484f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
485f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
486f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
487f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
488f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
489f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
490f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
491f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
492f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
493f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
494f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
495f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
496f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
497f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
498f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
499f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
500f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
501f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
502f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
503f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
504f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
505f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
506f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
507f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
508f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
509f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
510f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
511f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
512f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
513f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
514f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
515f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
516f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
517f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
518f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
519f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
520f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
521f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
522f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
523f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
524f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
525f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
532f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
533f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
554038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
559e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
568916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
573916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, Contacts.TIMES_CONTACTED)
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
585f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
591ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
613a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.IS_RESTRICTED)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
626a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.IS_RESTRICTED)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
6763d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
6773d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
6842530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
687ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
698f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
701f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
704f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
705c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
709f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
710f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
712ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
716f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
717f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ")")
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Contacts.HAS_PHONE_NUMBER + ")")
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
729373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
737eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
77582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7971b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
806d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
816778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
817778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
8197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
8209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
8219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
8229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
8239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
8249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
8259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
8262526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
8272526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
828bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
829bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
830bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
831bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
83251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
83303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
83403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
83503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
83603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
83703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri 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);
104805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1049bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
105015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
10513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
105249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
10534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
10544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1055767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
105651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
105751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
105804b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
105915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
106015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
10614cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
106204b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
10634cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
10644cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
106551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
1066cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
10675b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
106815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
10695b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1070f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
10715b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1072bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
1073bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1074bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE,
10756d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForEmail(context, mDbHelper, mContactAggregator));
1076bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
10776d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForIm(context, mDbHelper, mContactAggregator));
1078bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE,
10796d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForOrganization(context, mDbHelper, mContactAggregator));
1080bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE,
10816d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoneNumber(context, mDbHelper, mContactAggregator));
1082bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
10836d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNickname(context, mDbHelper, mContactAggregator));
1084bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
10856d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredName(context, mDbHelper, mContactAggregator,
1086bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
1087bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
10886d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredPostal(context, mDbHelper, mContactAggregator,
1089bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
1090bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE,
10916d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForGroupMembership(context, mDbHelper, mContactAggregator,
1092bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
1093bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE,
10946d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoto(context, mDbHelper, mContactAggregator));
10956d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov        mDataRowHandlers.put(Note.CONTENT_ITEM_TYPE,
10966d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNote(context, mDbHelper, mContactAggregator));
1097bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1098bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1099bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1100bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1101bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1102bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1103bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1104bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1105bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1106bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1107bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1108bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1109bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1110bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1111bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1112bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1113bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1114bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1115bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
111615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
111715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
111815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
111915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
112015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
112115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
112215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
112315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1124bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
112515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
112615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1127bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1128bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1129bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1130bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1131bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1132bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1133bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1134bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1135bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1136bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1137bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1138bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
113915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
114015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
114115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
114215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
114315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
114415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
114515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
1146bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
1147bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1148bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1149bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1150bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1151bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1152bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1153bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1154bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1155bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1156bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1157fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1158fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1159fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1160fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1161fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1162bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1163bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1164bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1165bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1166bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1167bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1168bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
116905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
117005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
117105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
117205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
117305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1174bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1175bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1176bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1177bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1178bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1179bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1180bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1181bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1182bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1183bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1184bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1185bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
11864cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
11874cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
118853fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
11893826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
11903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
11914f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
11924f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
11934f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1194fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
11954cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
119651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
119751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
119851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
119951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
120051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
120151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
120251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
120351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1204bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1205f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1206f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1207f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1208f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1209f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1210f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
121151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
121251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
121351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
121451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
121551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
121651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
121751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
121851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
121951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
1220bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, currentLocale);
1221bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1222bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1223bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
122451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1225fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1226fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1227fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1228fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1229fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1230fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1231fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1232fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
1233fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1234fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
1235fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1236fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1237fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
1238fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1239fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
1240fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1241fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1242fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1243fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1244fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
124505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
124605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
124705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
124805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1249bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1250bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
125151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
125251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
12533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
12543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
12553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
12563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
12573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
12583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
12593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mContactsAccountCount == 0
126049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                && DatabaseUtils.queryNumEntries(mDbHelper.getReadableDatabase(),
126149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                        Tables.CONTACTS, null) == 0) {
12623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
12633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
12643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
12653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
12663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
12673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
126831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1269de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1270b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1271b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
127231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
127331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1274013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1275013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1276013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1277013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
12785df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
12795df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
12805df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
12815df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
12825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1283ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
128472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
128572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
128672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
128772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
12885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
12895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
12905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
12915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
12923d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
1293b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1294b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
12953d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
12963d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1297568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1298568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1299568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1300568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1301568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1302bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1303568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1304bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1305bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1306bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1307568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1308bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1309bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, mCurrentLocale);
1310bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1311568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1312bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1313bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1314bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1315bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1316bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1317bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1318568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1319568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1320bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1321bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1322bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1323bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1324bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1325bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1326bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1327bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1328b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
1329b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1330b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1331bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1332bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1333bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1334bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1335bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1336bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1337bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1338bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1339bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1340bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1341bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1342bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1343bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1344bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1345bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1346bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1347bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1348bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1349bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1350bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1351bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1352bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1353bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1354bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1355bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1356bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1357bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1358bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1359bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
13603d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
13613d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
13623d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1363568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
13640e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
13653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
13663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1367bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1368bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1369bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1370bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1371bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1372bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
13733d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
13743d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
13753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1376bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1377bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
13783d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
13793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1380a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1381a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1382a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1383a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1384b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
13853826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1386a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1387a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1388568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
138915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1390568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1391568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1392568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1393568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1394568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
139515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
139615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
139715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
139815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
139915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
140015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
140115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
140215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
140315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
140415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
140515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1406ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1407568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1408568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1409568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1410568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1411568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
141215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1413568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1414568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1415568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1416568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1417568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
141815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
1419bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
1420bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
1421bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
1422bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
1423bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
1424bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
1425bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
1426bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1427bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
1428bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
1429bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
1430bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
1431bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1432bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
143315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1434568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1435568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1436568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1437568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1438568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
143915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1440568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1441568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1442568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1443568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1444568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1445568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
144615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1447568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1448568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1449568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
14504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
14517b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
14527b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
14537b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        return super.bulkInsert(uri, values);
14547b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
14557b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
14567b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
1457285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1458bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1459b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1460b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1461285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
14621ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1463d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1464b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1465b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1466285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1467285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1468285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
14691129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1470bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1471b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1472b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1473285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1474b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
14751ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
14761a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
14771a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1478b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
14791a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
14803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
14813826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
14823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
14833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
14843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
1485b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1486b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1487b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1488bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1489b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1490b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
14911129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1492d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (long rawContactId : mTransactionContext.getInsertedRawContactIds()) {
14937e2635fa663312adb2bc9d04f50a6bb54c6cc5f4Dmitri Plotnikov            mContactAggregator.updateRawContactDisplayName(mDb, rawContactId);
1494d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
1495285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1496b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1497d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> dirtyRawContacts = mTransactionContext.getDirtyRawContactIds();
1498d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
1499a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1500a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1501d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
1502a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1503a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
1504a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1505a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1506d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> updatedRawContacts = mTransactionContext.getUpdatedRawContactIds();
1507d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
1508a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1509a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1510d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
1511a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1512a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
1513b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1514b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1515f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        Set<Long> staleRawContacts = mTransactionContext.getStaleSearchIndexRawContactIds();
1516f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        if (!staleRawContacts.isEmpty()) {
1517f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleRawContacts);
1518f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        }
1519f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov
1520d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (Map.Entry<Long, Object> entry : mTransactionContext.getUpdatedSyncStates()) {
1521b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
15229d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
15239d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
15249d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
15259d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
1526b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1527b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1528d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1529b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1530b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1531a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1532a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1533a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1534a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1535d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
1536b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1537a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1538b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1539a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1540a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1541285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1542285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1543285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1544cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
154581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
154681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
154781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
154881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
154981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
155081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
155181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
1552cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
1553568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
155451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
15553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
15563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
15573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
15583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
155951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
156051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1561f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
15623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
15633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
15646d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
15656d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                    getContext(), mDbHelper, mContactAggregator, mimeType);
15663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
15673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
15683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
15693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
15703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1572de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
1573bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
15741129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
1575b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1576f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1577f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
1578f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
1579f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1580a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
1581a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
158235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1583a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
158435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
1585b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
158635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
158735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1588d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
1589d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
15906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
15916bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
15926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
15935ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
1594dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                id = insertRawContact(uri, values, callerIsSyncAdapter);
1595f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1596a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1597a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1598a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
15995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
16005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
1601f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1602f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1603a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1604a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1605a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1606a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
1607f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1608f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1609a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1610a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1611a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1612ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1613f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
1614f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1615ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1616ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1617ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1618eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
16195aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
162043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
1621eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
1622eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
1623eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
162482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
162582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
16261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
16271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
16281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1629a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
163081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
1631f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
1632a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1633a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
16347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
16357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
16367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
16377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1638de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
1639a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1640a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1641a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1642e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
1643e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
1644e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
1645e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
1646e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
1647e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
1648e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
1649e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
1650e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
1651e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
1652e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
1653e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
1654e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
16557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
1656e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
1657f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
1658f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
1659e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
1660f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1661f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1662f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
1663e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
1664e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
1665e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1666e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
1667e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
1668fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
1669fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
1670e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
1671e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1672e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
1673e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
1674e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
1675e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
1676e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1677e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
1678e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
1679e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
1680e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
1681e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
1682fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
1683fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
1684e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
1685e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
1686e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
1687f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
1688f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
1689e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
1690f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
1691f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
1692e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
1693e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
1694f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
1695f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1696e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
1697f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
1698f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
1699f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
1700f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
1701035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1702f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1703e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
17047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
17057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
1707d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
17086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
17096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
17106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
17116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
1712d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
1713de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
17146bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
17156bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
17166bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
1717a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
1718a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1719f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
1720f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
1721dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
1722a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1723a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1724dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
1725f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
1726f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
1727f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
1728f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1729e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
17307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17313d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
17323d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
1733f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
17343d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
17353d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1736f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
1737f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
1738f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
1739f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
1740f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
1741f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
1742285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1743285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
1744d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactInserted(rawContactId, account);
1745f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1746dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
1747dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
1748dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
1749dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
1750dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
1751dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1752dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1753dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
17543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
1755023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
1756a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1757a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1758dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
1759dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
1760dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
1761dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
1762dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
1763dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1764dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1765dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1766dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
1767dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
1768dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
1769dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
1770dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
1771dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
1772dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
1773dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
1774dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1775dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
1776dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
1777dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
1778dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1779dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1780dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1781dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
1782dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
1783dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
1784dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
1785dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
1786dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
1787dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
1788dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
1789dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1790dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1791dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1792dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1793dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
1794dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
1795dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
1796dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
1797dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
1798dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
1799dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
1800dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1801dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1802dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
1803dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
1804dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
1805dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
1806dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
1807dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
1808dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1809dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1810a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1811a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
1812a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1813a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1814a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1815a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1816f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
1817a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
1818de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
1819de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
182067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
1821de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
182220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1823de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
1824de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
1825de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
1826b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
1827de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
1828de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
1829508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
1830de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
1831de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
1832de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
1833de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
1834de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
18354097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
1836b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
1837de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
1838a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1839a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
1840d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        id = rowHandler.insert(mDb, mTransactionContext, rawContactId, mValues);
1841f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
1842d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.markRawContactDirty(rawContactId);
1843a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1844d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactUpdated(rawContactId);
1845a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
18464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
18474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1848ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
18497e2635fa663312adb2bc9d04f50a6bb54c6cc5f4Dmitri Plotnikov        mContactAggregator.updateRawContactDisplayName(db, rawContactId);
1850d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
1851d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
18529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
185320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
185420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
1855f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
185620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
185720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1858de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
1859de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
1860f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS,
1861f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
1862de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
1863de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
1864f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
1865f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
1866a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
1867d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                count += rowHandler.delete(mDb, mTransactionContext, c);
1868f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
1869d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                    mTransactionContext.markRawContactDirty(rawContactId);
187088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
187120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
187220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
1873de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
187420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
187520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
187620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
187720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
187820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
187988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
188088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
188188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
188220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
1883f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
188488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
188588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
18864da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
1887f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
18884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
1889f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
189020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
189120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
189220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
189320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
189420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1895f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
189620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
189720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
189820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
189920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
190020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
190120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
190220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
190320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
190420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
19057a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
190620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
190720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
190820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1909a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
1910d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return rowHandler.delete(mDb, mTransactionContext, c);
191120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
191220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
191320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
191420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
191520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
191620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
1917ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
1918ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
1919f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
1920f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
1921f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
1922f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1923e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
1924ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1925ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
1926f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
192767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
1928f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
192967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
1930f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
1931ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1932dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
1933dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
1934dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
1935dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1936f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
1937f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
193873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
193973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1940f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
1941ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
1942dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
1943dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
1944dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
1945dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
1946dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
1947dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
1948dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
1949dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
1950dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
1951dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
1952dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
1953dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
1954dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1955dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
1956dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
1957dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
1958892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
1959892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
1960892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
1961892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
1962892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
1963d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mTransactionContext.markRawContactDirty(rawContactId);
1964892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
1965dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
1966892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
1967892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
1968dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1969dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1970dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1971f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
19721a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
1973ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
1974ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
1975ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
1976ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1977ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
19785aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
1979e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
19805aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
19811a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
19821a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
1983e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
19841a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
1985e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
1986e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
1987e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
1988ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
198982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
19901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
199182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
199282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
19930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
19944dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
19954dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
19960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
199782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
19984dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
19994dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
20004dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
20014dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
20021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
20031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2004dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2005dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
200682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2007f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
20082526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
2009dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2010dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2011dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
20122526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
20132526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
20141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2015dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2016dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
20170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
20180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
20190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
20200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2021dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2022dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2023dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
20242a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mDbHelper.getMimeTypeIdForIm());
2025dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
20262a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mDbHelper.getMimeTypeIdForEmail());
2027f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2028f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2029f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2030f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2031f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2032f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2033f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
20342526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
20352526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
20362526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
20372526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
20382526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
20392526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
20402526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
20412526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
2042dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
20432526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
20442526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2045dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
20462526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
20472526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
2048dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
20492526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
20502526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
20512526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
20522526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
20532526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
20542526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
2055dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
20562526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
20572526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2058dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2059dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
20601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
206182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
20622526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
20632526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
2064dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
206570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
2066f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
206770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
20681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
20691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2070de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
20712526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
20724394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
20731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
207467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
20755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2076e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
20771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
20781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
20791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
20801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
20811f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
208231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
208331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
208431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
20851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
20861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
208782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2088a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2089a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2090a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2091a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2092a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2093a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2094a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
209582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2096a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2097a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
209882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
209982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
210082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
210182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
210282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2103a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
210482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
210582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
2106aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
2107aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
21081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2109a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2110a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2111a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2112e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
21130a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
211482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
211582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
21160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
21170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
21180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
21190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
21200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
21210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
21220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
21230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
21240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
21250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
21260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
21270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2128a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
212978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteStatusUpdate(dataId);
213082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
213182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
213278fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.replaceStatusUpdate(dataId, timestamp, status, resPackage, iconResource,
213378fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
2134a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
213578fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.insertStatusUpdate(dataId, status, resPackage, iconResource,
213678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
2137e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2138e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2139bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2140a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2141f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
2142a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2143a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2144a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
21451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
21461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
21474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2148de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2149bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2150b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2151b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2152b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2153f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2154f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2155508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2156508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
215735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2158b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
215935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2160b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2161b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2162b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2163b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2164b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2165b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2166cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2167cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2168cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2169cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2170cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2171d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2172d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2173dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
21746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
21756bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
21769fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
21772e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
21782e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
21792e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
2180fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2181fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
21822e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
21832e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
21842e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
2185dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
21862e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
21872e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
21889fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
21899fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
21909fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
21919fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
21929fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
21939fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
2194a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
21959fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
21969fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
21979fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
21989fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
21999fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
22009fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
22019fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
22029fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
220360de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
22049fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
22059fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
22069fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
22079fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
22089fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
22099fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
2210dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
22119fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
22129fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
22139fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
22149fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
22159fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
22169fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
22179fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
22189fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
22199fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
22209fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
22212971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
22222971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
2223fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
2224fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
2225e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
22262971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
22272971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
22282971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2229fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
2230fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
2231fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
22322971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
22332971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
22342971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
22352971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
22362971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
22372971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
22382971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
22395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
22402971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2241fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
2242fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
2243508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2244508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
224520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2246f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2247944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2248f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
224920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
225020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
225148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
225248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
225348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
225448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2255508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2256f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
22574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
22584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
2259ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2260ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2261ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2262f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
22635aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
22642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
22652971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
22662971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
22672971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
22682971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2269e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
22702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
22712971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
22725aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
22732971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
22742971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
22752971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
22762971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
227781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2278f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
227981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
22802971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2281508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2282508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2283eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
228443880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2285e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
2286eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2287eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
228882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
22890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
22901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
22911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
229281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
229381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
22943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
229581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2296508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
22974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
22984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
22991c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
2300ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2301b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
230294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2303de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
230494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
230594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
230694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
230794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2308f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2309de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
231094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
231194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
231294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2313f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2314de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
231594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
231694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
23171a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
231894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
231994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
232094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
23215aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2322e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
23231a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
2324e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2325e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2326e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2327dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
232896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
2329cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
233096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
233196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
2332cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2333cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2334cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2335dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
2336cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2337cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2338cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2339cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2340cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
23413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
23423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2343cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2344cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2345cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2346fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
23473389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
23483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
23493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2350f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
235114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2352fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
2353fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
2354fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
235533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2356b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2357dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
235833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
235933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
236033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
23610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
23629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
23639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
23649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
23659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
23669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
23679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
23689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
23699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
23700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
23710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2372dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
237381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
237481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2375cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2376cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2377cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2378cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2379cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2380cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2381dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
2382cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2383cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
23844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2385de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2386de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2387bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2388b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2389b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2390b5a4add17815167d20a90645779df34cdf45280dFred Quintana
239135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
239200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
239300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2394b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2395b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
23961129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
2397d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.syncStateUpdated(rowId, data);
2398b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2399b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2400b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2401f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2402f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
240300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
240435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2405b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2406b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2407b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2408b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2409b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2410b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2411b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2412b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2413b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2414b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2415b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
241635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2417d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2418dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
241900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
242000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
242100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2422d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2423dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
2424c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2425c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2426c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
24272e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
24282e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
24292e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
24302e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
24312e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
2432fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2433fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
24342e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
24352e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
24362e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
2437dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
24382e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
24392e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
24402e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
24417d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
24427d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
24437d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
24447d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
24457d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
24467d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
24477d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
24487d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
24497d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
24507d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
245120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2452944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2453f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
245481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2455f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
245681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
245720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
245820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2459c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
246048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
246148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
246248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
246348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2464f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
246581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2466f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
246781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
246800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
246900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
24707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
24725ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
2473dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
24747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
24757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
24767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24775ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
247833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
24794529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
24804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
24814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
2482dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
2483dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
24844529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
24854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
2486dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
2487dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
24884529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
24897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
24907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
24917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2492ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
24935aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
2494f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
249581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2496f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
249781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2498ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2499ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2500ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2501ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2502ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
25034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
25044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
250573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
25065aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
25075aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
250881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2509f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
251081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2511ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2512ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2513ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2514127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
2515de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
2516b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2517b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2518b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2519eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
2520e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
2521e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
252243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2523eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2524eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2525eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
25269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
25279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
25289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
25299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
25309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
253172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
2532bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
253372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
2534d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
2535d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
2536d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
253781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
253881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2539f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
254081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
254100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
254200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
254300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
25444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
25454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
25469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
25479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
25489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
25499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
25509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
25519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
25529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
25539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
25549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
25559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
25569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
25579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
25589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
25609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
25619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
25629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
25639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
25649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
25659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
25669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
25679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
25689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
25699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
25719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
25729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
25739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
25749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
25759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
25769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
25779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
25789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
25799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
25809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
25829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
25839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
25849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
25859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
25869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
25879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
25889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
25899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
25909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
25919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
25929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
25939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
25949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
25959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
25969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
25979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
25989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
25999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
2600aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
2601aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
26029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
26039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
26049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
26055aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
2606f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
260773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2608ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2609ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
261073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
2611f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
261273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
261373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
261473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
261573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
261673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
261773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
261873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
261973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2620ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
26211a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
26221a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
262394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
26246ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
26251129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
26266ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
2627e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
26286ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
26296ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
26306ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
26316ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
26326ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
26336ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
26346ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
26356ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
26366ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
2637ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
26386ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
26396ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
26406ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
26416ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
26426ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
26436ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
26446ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
26456ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
264694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
264794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
264894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
2649b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
2650b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
2651e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
26521a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
26531a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2654e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2655e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2656e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2657e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2658dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
2659dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
26604529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
26614529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
26624529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
26634529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
266473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
266597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
266697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
266797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
266897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
266997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
26704529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
2671b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
267251bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
26734529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
26744529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
26754529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
26764529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
2677dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
26784529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
26794529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
26804529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
26814529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
26824529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
26834529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
26844529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
26854529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
26864529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
2687dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
2688dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
268996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
269096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
269119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
269219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
269319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
2694ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
2695ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
269619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
269719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
269896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
269919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
270019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
270119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
2702ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
2703ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
270419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
270519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
270619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
270719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
270819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
270919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
271019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
2711f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
271296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
27135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
2714f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
2715f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
2716f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
2717f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
2718f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
2719f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
272069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
2721f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
2722f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
2723433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
2724dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
2725dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
2726dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
2727dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
27284529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
2729dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2730dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
2731dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
2732dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
2733dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
2734dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
2735dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
2736dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
2737dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
2738dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
2739dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2740dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2741dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2742dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
2743dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
2744dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
2745dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
2746433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
2747dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2748285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
27492b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
2750285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
2751f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
2752f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
2753f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
2754f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
2755f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
275678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.resetNameVerifiedForOtherRawContacts(rawContactId);
2757f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
2758f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
2759f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
276019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
2761d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mTransactionContext.rawContactInserted(rawContactId,
2762d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        new Account(accountName, accountType));
276319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
27645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
27655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
276633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
276733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
2768321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
2769f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
277020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
277120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
277220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
27735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
277420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
277520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
277620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
277720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
277820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
2779b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
278020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
278120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
278297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
278397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
278497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
278597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
278697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
2787653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
278820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2789653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2790653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
2791f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(uri, DataRowHandler.DataUpdateQuery.COLUMNS,
2792f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2793653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
2794653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
2795f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
279620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2797653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
2798653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
279920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
280020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2801653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
280220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
280320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2804f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
2805653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
2806653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
2807321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
2808653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
2809f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
2810a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2811d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (rowHandler.update(mDb, mTransactionContext, values, c, callerIsSyncAdapter)) {
2812813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
2813813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
2814813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
2815a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2816321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
2817321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
28188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
2819dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
28208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
2821b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
28228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
28238c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
28248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
28258c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
28268c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
2827dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
28288c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
28298c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
28308c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
28318c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
28328c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
28338c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
28348c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
28358c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
28368c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
2837dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
2838dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
2839d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
28408c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
2841b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
2842d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
2843b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
2844d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
2845b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
2846d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
2847b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
2848d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
2849b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
2850d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
2851d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2852d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
28538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
2854d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
2855d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
2856d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
28578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
2858c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
28598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
2860c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
2861c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
28624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
286397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
286497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
28658c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
2866dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
2867dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
2868dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
2869dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
2870dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
2871dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
2872dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
2873dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
2874dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
2875dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2876dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
2877dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
2878dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2879dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2880dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
28818c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
28828c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
28838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
2884b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
28858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
2886b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
28878c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
2888b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
28898c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
2890b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
28918c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
2892b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
28938c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
28948c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
28959b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
28966e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
28979b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
28989b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
28999b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
29009b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
29019b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
29029b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
2903f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2904d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2905127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
2906127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
29070c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
29080c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
290980c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
2910ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
2911ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
29120c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
29130c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
29140c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
29150c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
29160c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
29170c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
2918b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
2919127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
29200c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
29214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
29224da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
29230c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
29244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
29254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
29260c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
29276bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
29286bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
29290c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
29300c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
29310c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
29320c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
2933127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
2934127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
29353389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
293669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
293769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
293869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
293969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
2940dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
29410dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1);
29420dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2);
2943127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
2944127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
2945127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
2946127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
2947b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
2948b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
294970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
2950bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
29513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
29523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2953bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
2954f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
2955e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
2956627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
295749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
295870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
295970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
2960dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
2961743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
2962743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
2963743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
2964743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
2965e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
2966743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
2967743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
2968743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
2969743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
2970743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
297148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
2972627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
2973743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
2974627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
2975627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
2976627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
297770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
297870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
297933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
2980e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
2981e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
2982e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
2983e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
2984e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
2985e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
2986e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
2987e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
2988e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
2989e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
2990e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
2991e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
2992e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
2993e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
2994e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
2995e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
2996e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
2997e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
2998e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
2999e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3000e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
3001e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
3002e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
3003e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3004e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
3005e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
3006e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
3007d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
3008d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
3009d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
3010d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
30114458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
3012e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
3013e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
301433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
301533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
3016e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
301733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
301833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
301933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
302033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
302169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
302269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
302369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
302433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
302533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
302669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
302769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
302833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
302933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
303033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
303133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
303233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
303333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
303433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
303533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
303633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
303733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
303833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
3039e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
304033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
304133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
3042e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
3043e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
3044e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
304570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
304670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
304770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
304870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
304973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
30503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
30513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
30523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
30533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
30543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
30553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3056afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
305770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3058619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
30593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
30603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
30613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
30623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
30633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
30643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
30653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
30663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
30673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
30683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
30693826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
30703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
30713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
30723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
30733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
30743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
30753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
30763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
30773826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
30783826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
307972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
3080bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
3081d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3082d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3083619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3084627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
3085627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
3086dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
3087743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
3088743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
3089743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
3090627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
3091627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
3092dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
3093627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
3094627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3095627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3096627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
3097627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
3098627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
3099627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
3100627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
31016ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private static class DirectoryCursorWrapper extends CursorWrapper
31026ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            implements CrossProcessCursor {
31036ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        private final CrossProcessCursor mCrossProcessCursor;
31046ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31056ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        public DirectoryCursorWrapper(Cursor cursor, CrossProcessCursor crossProcessCursor) {
31066ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            super(cursor);
31076ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            mCrossProcessCursor = crossProcessCursor;
31086ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31096ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31106ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        @Override
31116ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        public void fillWindow(int pos, CursorWindow window) {
31126ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            mCrossProcessCursor.fillWindow(pos, window);
31136ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31146ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31156ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        @Override
31166ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        public CursorWindow getWindow() {
31176ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return mCrossProcessCursor.getWindow();
31186ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31196ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31206ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        @Override
31216ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        public boolean onMove(int oldPosition, int newPosition) {
31226ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return mCrossProcessCursor.onMove(oldPosition, newPosition);
31236ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31246ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
31256ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
31274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
31284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
312915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
313015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
313115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
3132d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
3133385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
3134385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1);
3135385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
3136385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder,
3137385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                    Directory.DEFAULT);
3138d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
3139385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder,
3140385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                    Directory.LOCAL_INVISIBLE);
3141d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3142d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3143d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
3144d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
3145a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
3146a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
3147d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3148d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3149d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
3150d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
3151d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
3152d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
3153d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
3154d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
3155d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3156d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
3157d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
3158d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
31592e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
31602e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
31612e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
31622e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
31632e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
31642e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
3165d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
316609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
316709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
316809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
316909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
317009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
3171332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
3172d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
31736ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31746ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
31756ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
31766ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31776ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31786ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
31796ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (crossProcessCursor != null) {
31806ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return new DirectoryCursorWrapper(cursor, crossProcessCursor);
31816ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
31826ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return matrixCursorFromCursor(cursor);
31836ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31846ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
31856ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31866ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
31876ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
31886ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
31896ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
31906ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
31916ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
31926ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
31936ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
31946ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
31956ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
31966ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
31976ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
31986ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
31996ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
32006ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
32016ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
32026ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
32036ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
32046ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
32056ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
32066ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
3207332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
32086ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
3209d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3210d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3211d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
3212d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
3213d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
3214d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
3215d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
3216d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
3217d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
3218d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3219d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
3220d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
3221d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
3222d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
3223d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3224d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3225d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
3226d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
3227d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
3228d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
32294458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
32304458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
32314458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
323249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
323349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
32344458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
32354458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
32364458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
32374458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
32384458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
32394458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
32404458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
32414458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
32424458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
32434458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
32444458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
32454458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
32464458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
3247d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
32484458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
3249d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3250d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
32514458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
32524458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
3253d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3254d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
325572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
32564458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
32574458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
32584458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
325972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
326072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
3261d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    public Cursor queryLocal(Uri uri, String[] projection, String selection, String[] selectionArgs,
3262385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                String sortOrder, long directoryId) {
3263bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3264bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
3265bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
32660b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
3267b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
326835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3269d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
32701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
3271c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
3272c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3273619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
3274619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
3275a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
32764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
327735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3278b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
327935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
328035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3281d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3282763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3283385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
3284619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
3285619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
3286619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3287d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
32884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3289763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
32904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
32914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
32926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
32936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
32946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
32955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
32965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
32975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
32985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
32995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
3300fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3301fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
33025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
3303a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
33045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
33055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
33065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
33075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3308763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
3309a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3310a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3311a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3312a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
3313a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
33145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
33155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
33165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
33175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3318763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
33194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
33204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
33214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
33225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
33235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
33245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
33252149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
33262149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
33272149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
33282149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
33292149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
33302149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
33312149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
33322149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
33332149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
33342149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
33352149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
33362149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
33372149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
3338a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
3339a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3340a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3341a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
3342a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
33432149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
33442149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
33452149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
33462149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
33472149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
33482149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
33492149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
33502149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
33512149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
33522149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
33532149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
33542149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
33552149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
3356f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
3357f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
335842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
3359763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
3360f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
33614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
33624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
33634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
3364f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
3365f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
3366f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
336742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
336842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
336942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
337042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
337142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
337242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
337342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
337442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
337542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
337642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
3377ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
3378916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
3379ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
3380916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
3381ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
3382916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(qb, uri, projection, filterParam);
3383385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
3384ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3385ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3386ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3387ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
3388ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
33894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3390ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3391d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
33924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
33934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3394e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
33955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
33964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
33974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
33984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3399763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3400ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
34015e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
34025e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
34035e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
3404dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    starredProjection =
3405dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
3406dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    frequentProjection =
3407dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
34085e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
34095e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
34104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
34114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
34124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3413d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
34145e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
34155e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
34164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3417d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3418d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3419d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3420763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
34214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
34224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3423d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
34245e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
34255e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
3426d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3427d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
34284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3429d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3430d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3431d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3432d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
34334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
34344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3435d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3436d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3437d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3438d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3439d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3440d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3441ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3442763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3443b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
344471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
34454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3446b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3447b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3448b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3449b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
3450a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
34514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
345282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
34534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
34544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
34556bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
34566bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
345700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3458a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
34593653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
346082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
34614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
34624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
34633653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
34643653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
34653653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
34663653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
3467a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
3468a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
3469a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
3470a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
3471a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
3472a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
3473a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3474a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3475a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
3476a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
3477a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
3478a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
3479a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
3480a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3481a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
3482a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
3483a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
3484a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
3485a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
3486a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3487a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
3488a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
3489a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3490a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3491a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3492a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
3493a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
3494a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
3495a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
3496a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
3497a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
3498a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3499a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
3500a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
3501a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
3502a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
3503a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
3504a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3505a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
35064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
350782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
350889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
35092815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
35102815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
35112815f58f72f109790585931f601a63ddc02536a5Evan Millar
351248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
351382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
35144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
351548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
35164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
351748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
351848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
351948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3520ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
352182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
352289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3523ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
35244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
35254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3526a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
35275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
352845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
35295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
35305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
35315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
35325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
35337318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
35345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
353545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
35365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
35375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3538892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
3539892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
35405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
35415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
35425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
35435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
3544892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
3545892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
3546892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
3547892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
3548892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
354945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
355045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
355145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
355245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
355345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
355445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
355545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
35565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
35575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3558a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
3559ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
35605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3561a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3562a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3563a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
3564ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3565ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3566ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
35674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
356882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
356989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
35704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
35714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
35724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
357348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
357482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
35754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
35764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
35774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
357848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
357948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
358048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
35815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
358282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
358389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
35844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
358508768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
358608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
358708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
358808768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
35894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3590ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3591ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3592ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
35935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
359482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
359507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
359607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
359707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
359807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
359907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
360007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
360107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
36025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
360307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
360407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
360507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
360607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
360707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
360807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
360907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
361007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
361107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
36122a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
36132a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(mDbHelper.getMimeTypeIdForEmail());
36142a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
361507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
361620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
361720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
361820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
361907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
362007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            /*
362107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * Using a UNION instead of an "OR" to make SQLite use the right
362207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * indexes. We need it to use the (mimetype,data1) index for the
362307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * email lookup (see above), but not for the name lookup.
362407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * SQLite is not smart enough to use the index on one side of an OR
362507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * but not on the other. Using two separate nested queries
362607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * and a UNION between them does the job.
362707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             */
362807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            sb.append(
362907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " UNION SELECT " + Data._ID +
363007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " FROM " + Tables.DATA +
36312a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                                    " WHERE +" + DataColumns.MIMETYPE_ID + "=");
36322a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            sb.append(mDbHelper.getMimeTypeIdForEmail());
36332a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            sb.append(" AND " + Data.RAW_CONTACT_ID + " IN ");
36347318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
363520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
36365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
36375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3638a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
36395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
36405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
3641a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3642c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov                    sortOrder = EMAIL_FILTER_SORT_ORDER;
3643a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
36445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
36455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
36465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3647ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
364882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
364989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
365089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
3651ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3652ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3653ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
365448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
365582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
365748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
365848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
36594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
366048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
366148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
366248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
36635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
3664763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
36654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
36664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
36674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
36695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
3670763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
36714da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
36724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
36734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
36744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
36754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36765ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
36775ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
367882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
36804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
3681e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3682e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3683e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
3684e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
368582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3686e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3687e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3688e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
36894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
369082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
36924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
3693a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
3694a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
3695a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
3696a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
36974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3698a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
3699a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
3700a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
3701892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
3702a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
3703a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3704e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
3705e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
3706e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        mDbHelper.getCurrentCountryIso());
3707892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
3708892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
3709892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
3710e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
3711e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
3712e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
3713e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
3714a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
3715a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
3716a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3717ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
3718b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3719ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
372089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3721ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3722ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3723ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3724ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3725b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3726ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
37274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
37284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
3729ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3730ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3731ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3732ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
3733b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
3734ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
373589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
373689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
3737ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3738ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3739ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3740b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
37410c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
3742b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
3743b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3744b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3745b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
374631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
3747d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
37482d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
37492d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
37502d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
37512d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
375231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
3753d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
3754d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
375531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
375631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
375731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
375831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
37595b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
37605b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
37615b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
37625b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
37635b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
37645b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
37655b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
37665b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
376776dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
37685b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
37695b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
37705b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
37715b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
37725b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
37735b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
37745b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
3775763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
37767581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
37777581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
37785b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
377931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
378031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3781eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3782eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
3783eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
378489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3785e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3786e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
3787e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
3788b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
3789e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
379082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3791b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
3792e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3793e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
379482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3795b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
3796e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3797e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3798e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3799eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3800eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3801eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
380282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
38030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
38045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
38055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
38065ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
380782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
38080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
38094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
38104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
38115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
38125ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
38135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
3814c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
3815a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
3816c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3817c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3818c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
38192d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
38202d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, lookupKey, projection);
3821c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3822c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
38231b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
3824b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
38251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
38261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
38271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
38281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
3829b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
38301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
38311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
38321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
38331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
38341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
3835b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
38361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
38371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
38381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
38391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
38401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
3841b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
38421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
384371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
38441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
38451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
38461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
384746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
3848a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
384946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
385046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
385146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
385246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
385346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
3854a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
38554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
38564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
385746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
385846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
385946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
386009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
386109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
386209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
386309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
3864d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
3865d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
3866d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
3867d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3868d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3869d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3870d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
3871385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
3872d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
3873d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
3874385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
3875d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
3876d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3877d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3878d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
38797a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
38807a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
38817a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
38827a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
38834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
3884f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
3885c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
38864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
38874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
38887f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov        qb.setStrictProjectionMap(true);
38897f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
3890ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
3891ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
3892ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
3893ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
3894ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
3895ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
38965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
38975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
38995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
39005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
3901038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
3902038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
3903038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
3904038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
39055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
39065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
39074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
39084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
39094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
39104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
39114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
39124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
391309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
391409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
391509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
391609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
391709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
391809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
391909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
392009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
392109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
392209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
392309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
392409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
392509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
392609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
392709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
392809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
3929a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
3930a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
3931a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
3932a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
3933a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
3934a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
3935a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
3936a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
3937a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
3938a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
3939a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
3940a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
3941a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
3942a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
3943a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
3944a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
3945a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
3946a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
3947a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
3948a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
3949a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
3950a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
3951a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
3952a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
3953a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
3954a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3955a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
3956a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
3957a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
395809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
3959bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
3960bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
3961bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
3962bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
3963ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3964bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
3965bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
3966ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
3967ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3968bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
3969bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
3970bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
3971bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
3972de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
3973ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
3974ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3975ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
3976ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
3977ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
3978ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
3979ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
3980ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
3981ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
3982ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3983ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
3984ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
3985ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
3986ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
3987ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
3988ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
3989ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
3990ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
3991ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
3992ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
3993ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
3994ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
3995ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
3996ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
3997ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
3998ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
3999bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
4000ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
4001bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
4002bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "SUBSTR(" + sortKey + ",1,1) AS " + AddressBookIndexQuery.LETTER);
4003bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4004bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
4005bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
4006bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
4007bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
4008bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
4009bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
4010bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
4011ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
4012bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "GET_PHONEBOOK_INDEX(SUBSTR(" + sortKey + ",1,1),'" + locale + "')"
4013bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
4014ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
4015ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
4016ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
4017ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4018f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
4019ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
4020ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
4021ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4022ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
4023f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
4024ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
4025ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
4026bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
4027bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
4028bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4029bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
4030bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
4031bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
4032ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
4033f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
4034bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
4035bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
4036bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
4037bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
4038bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
4039bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
4040bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
4041bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
4042bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
4043bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
4044bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4045bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
4046bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
4047bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
4048bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
4049bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4050bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
4051bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
4052bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
4053ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4054ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4055e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return new AddressBookCursor((CrossProcessCursor) cursor, titles, counts);
4056ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
4057f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
4058ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4059ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4060ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
40612d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
406292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
406392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
406492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
406592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
40662d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
40672d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
40685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
40695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
40705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
407192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
407292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
407392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
407492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
407592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
407692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
407792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
407892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
407992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
408092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
408192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
408292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
408392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
408492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
408592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
408692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
408792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
408892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
408992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
40905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
40915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
40945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
40975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
40985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
41005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
41015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
41025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
41035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
41045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
41055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
41075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
41085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
41095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
41105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
41135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
41145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
41155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
41165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
41175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
411892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
41195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
41205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
41215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
41245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
41255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
41275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
41285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
41295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
41305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
41315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
41325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
41335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
41345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
41355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
41365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
413792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
413892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
41395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
41405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
41415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
41425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
41435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
41445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
41465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
41475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
41505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
415292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
415392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
41545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
41565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
41575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
41585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
415992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
41605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
41615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
41635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
41645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
416592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
41665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
416892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
416992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
417092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
417192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
41725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
41735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
417492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
417592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
417692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
41775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
417992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
418092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
41815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
418292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
418392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
418492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
418592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
418692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
418792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
418892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
418992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
419092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
419192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
419292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
419392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
419492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
419592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
419692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
419792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
419892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
419992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
420092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
420192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
420292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
42035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
420592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
420692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
420792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
420892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
420992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
421092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
421192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
421292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
421392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
421492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
421592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
421692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
421792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
421892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
421992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
422092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
422192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
422292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
422392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
422492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
422592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
42265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
42275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
42285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
42295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
423092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
423192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
42325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
42335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
42345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
42355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
42375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
42385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
42395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
42415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
42425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
42435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
42445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
42455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
42465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
42475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
42485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
42495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
42505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
425192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
425292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
425392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
42545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
42555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
42565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
42575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
42585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
42595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
42605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
42615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
42625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
42655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
42665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
426792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
426892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
426992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
427092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
427192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
427292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
427392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
427492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
427592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
427692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
427792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
4278ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
4279ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
4280ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
4281ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
42825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
42835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
42845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
42855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
42865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
42875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
42895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
42905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
42925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
42935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
42955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
42965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
42975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
42985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
42995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
43005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
43015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
43025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
43035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
43045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
43055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
43065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
43075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
43085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
43095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
43105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
43115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
43125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
43135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
43145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
43155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
43165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4317763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
4318763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
431982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4320916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
4321916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
4322916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
4323916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
4324916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4325916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
4326916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
4327916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
4328916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
4329916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
4330916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            String[] projection, String filter) {
4331916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
433203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
433303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
433403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
433503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
4336916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
4337916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
433803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        appendSearchIndexJoin(sb, uri, projection, filter);
433903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
434003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
434103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
4342916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
434303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
434403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            StringBuilder sb, Uri uri, String[] projection, String filter) {
434503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
4346916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
434703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET)) {
434803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String startMatch;
434903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String endMatch;
435003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String ellipsis;
435103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            int maxTokens;
4352916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
435303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
435403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
435503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
435603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
435703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
435803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
435903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
436003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (args != null && args.length > 0) {
436103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                startMatch = args[0];
436203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
436303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                startMatch = DEFAULT_SNIPPET_ARG_START_MATCH;
436403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
436503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
436603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (args != null && args.length > 1) {
436703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                endMatch = args[1];
436803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
436903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                endMatch = DEFAULT_SNIPPET_ARG_END_MATCH;
437003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
437103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
437203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (args != null && args.length > 2) {
437303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                ellipsis = args[2];
437403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
437503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                ellipsis = DEFAULT_SNIPPET_ARG_ELLIPSIS;
437603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
4377916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
437803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (args != null && args.length > 3) {
437903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                maxTokens = Integer.parseInt(args[3]);
438003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
438103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                maxTokens = DEFAULT_SNIPPET_ARG_MAX_TOKENS;
438203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
438303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
438403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(", " + "snippet(" + Tables.SEARCH_INDEX + ",");
438503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, startMatch);
438603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(",");
438703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, endMatch);
438803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(",");
438903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
439003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
439103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            // The index of the column used for the snippet, "content"
439203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(",1,");
439303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(maxTokens);
439403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(")" + " AS " + SearchSnippetColumns.SNIPPET);
4395916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
4396916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
439703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX + " WHERE ");
439803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
439903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (TextUtils.isEmpty(filter)) {
440003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append("0");     // Empty filter - return an empty set
440103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
440203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(Tables.SEARCH_INDEX + " MATCH ");
440303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, filter + "*");
44049c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
440503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
44069c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov
440703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (true) {
440803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            return;
4409916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
4410916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
441103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
441203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String address = mDbHelper.extractAddressFromEmailAddress(filter);
441303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (!TextUtils.isEmpty(address)) {
441403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append(DataColumns.CONCRETE_ID + " IN (" +
441503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                        "SELECT MIN(" + DataColumns.CONCRETE_ID + ")" +
441603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                        " FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
441703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                        " WHERE " + DataColumns.MIMETYPE_ID + " IN (");
441803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append(mDbHelper.getMimeTypeIdForEmail());
441903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append(",");
442003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append(mDbHelper.getMimeTypeIdForIm());
442103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append(",");
442203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append(mDbHelper.getMimeTypeIdForSip());
442303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append(") AND " + Data.DATA1 + " LIKE(");
442403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, address + '%');
442503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append(")" +
442603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                        " GROUP BY " + RawContactsColumns.CONCRETE_CONTACT_ID +
442703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                        ")");
442803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                return;
442903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
4430916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
4431916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
443203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        String normalizedFilter = NameNormalizer.normalize(filter);
443303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (!TextUtils.isEmpty(normalizedFilter)) {
443403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(DataColumns.CONCRETE_ID + " IN (");
4435c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
443603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            // Construct a query that gives us exactly one data _id per matching contact.
443703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            // MIN stands in for ANY in this context.
443803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(
443903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    "SELECT MIN(" + Tables.NAME_LOOKUP + "." + NameLookupColumns.DATA_ID + ")" +
444003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    " FROM " + Tables.NAME_LOOKUP +
444103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    " JOIN " + Tables.RAW_CONTACTS +
444203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    " ON (" + RawContactsColumns.CONCRETE_ID
444303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                            + "=" + Tables.NAME_LOOKUP + "."
444403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                                    + NameLookupColumns.RAW_CONTACT_ID + ")" +
444503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    " WHERE " + NameLookupColumns.NORMALIZED_NAME + " GLOB '");
444603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(normalizedFilter);
444703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
444803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                        " IN(" + CONTACT_LOOKUP_NAME_TYPES + ")" +
444903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    " GROUP BY " + RawContactsColumns.CONCRETE_CONTACT_ID +
445003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    ")");
445103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
445203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append("0");     // Empty filter - return an empty set
445303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
4454a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
445503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (isPhoneNumber(filter)) {
445603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String number = PhoneNumberUtils.normalizeNumber(filter);
445703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(" OR " + DataColumns.CONCRETE_ID + " IN (" +
445803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    " SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
445903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    + " FROM " + Tables.PHONE_LOOKUP
446003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
446103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(number);
446203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append("%'");
4463a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
446403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
446503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    mDbHelper.getCountryIso());
446603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (!TextUtils.isEmpty(numberE164)) {
446703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
446803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append(numberE164);
446903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                sb.append("%'");
447003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
447103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            sb.append(')');
447203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
4473a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
4474a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
4475916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void appendContactsTables(StringBuilder sb, Uri uri, String[] projection) {
4476763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4477f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4478763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4479763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4480d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4481763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4482763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
4483a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
4484a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
448582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
4486ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
4487763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
4488763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
4489763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4490f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4491763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4492763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4493d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4494763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4495763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
4496763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
4497763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
4498763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
4499763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
4500763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4501a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
4502a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(mDbHelper.getRawEntitiesView(shouldExcludeRestrictedData(uri)));
4503a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
450446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
450546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
450646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
450782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
450882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
450982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4510a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getDataView(shouldExcludeRestrictedData(uri)));
451182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
451282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
4513a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
4514a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
4515a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
4516a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
45173296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
451882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
4519f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
4520f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
4521f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
4522f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
4523f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
452482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
4525ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
4526ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
45270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
45280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
45290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4530b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
45310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
4532a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
4533a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
45340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4535a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
4536a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
4537a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
4538a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4539a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
4540a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
4541a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
4542a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getEntitiesView(shouldExcludeRestrictedData(uri)));
4543a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
4544a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4545a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
4546a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
4547a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
4548a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
4549a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4550a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
4551a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
4552a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
4553a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
4554a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4555a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
4556a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
4557a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
4558a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
4559a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
4560a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
4561a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
4562a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
4563a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
4564a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
4565a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
4566a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
45670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
4568a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
45690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4570a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
4571a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
4572b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
45730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
45740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
45750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
45760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
45770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
45780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
4579a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
4580a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
45810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
4582a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
4583a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4584a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
4585a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
4586a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
4587a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
4588a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
4589a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
4590a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
4591a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4592a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
4593a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4594a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
4595a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
4596a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
4597a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
4598a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
4599a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4600a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
4601a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4602385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    private void appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
4603385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
4604385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
4605385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
4606385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
4607385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
4608385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
4609385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
4610a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private boolean shouldExcludeRestrictedData(Uri uri) {
4611a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        // Note: currently, "export only" equals to "restricted", but may not in the future.
4612a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
4613a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.FOR_EXPORT_ONLY, false);
4614a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (excludeRestrictedData) {
4615a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return true;
4616a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4617a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4618a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4619a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4620a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (requestingPackage != null) {
4621a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4622a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4623a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4624a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return false;
46250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
46260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
46274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
4628f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
4629f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
4630e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
4631e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
4632e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
4633e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
4634fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4635fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
4636e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
4637e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
4638e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
4639e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
4640e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
4641e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
46424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
46434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
46444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
46454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
46464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
46474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
46484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
46494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
46504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4651e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
4652f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
4653f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
4654e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
4655e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
4656e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
4657e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
4658fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4659fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
4660e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
4661e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
4662e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
4663e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
4664e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
4665e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
4666e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
4667e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4668e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
4669e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
4670e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
4671e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
4672e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
4673e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
4674e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
4675e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
4676e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
4677e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
4678e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
4679e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
4680e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
46817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
4682c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
4683c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
4684c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
4685c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
4686c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
4687f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
46882e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
4689c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
4690c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4691c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4692c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
4693c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
4694c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
4695c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
4696c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
4697c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
4698c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4699c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
4700c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
4701c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
4702c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4703c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4704c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4705c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
470600ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
4707d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
470870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
470970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
4710fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
471170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
471270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
471370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
471470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
4715d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
471670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
471767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
47185ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
47195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
4720619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
4721619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
4722619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4723b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
4724f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
4725415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
4726415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
4727415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
4728b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
4729b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
4730a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
4731f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
4732e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " + RawContacts.CONTACT_ID + "=?",
4733e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
4734e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
4735b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4736e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
4737f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
4738e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
47394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
4740d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4741d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4742f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
474349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
474442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
474549d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(lookupContactIdByLookupKey(db, lookupKey));
474642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + "=?";
474742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
474842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
474942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
475042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
475142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
475242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                outputRawContactsAsVCard(localStream, selection, mSelectionArgs1);
4753f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
475442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
475542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
475642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
475749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
475842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
475942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
476042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
476142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
4762d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
4763d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
476442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
476542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
4766d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
476742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
4768d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
476942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
477049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                    inBuilder.append(lookupContactIdByLookupKey(db, lookupKey));
477142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
477242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
477342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
477442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
4775d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4776d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
4777d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
4778d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
4779d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
4780d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                outputRawContactsAsVCard(localStream, selection, null);
4781f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
4782d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4783b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4784b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
4785fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
4786fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
4787b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
4788b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
4789b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4790f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,
4791e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
4792e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
4793e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
4794e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
4795e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
4796e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
4797e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
4798e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
4799e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
4800e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
4801e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
480208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
4803f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
4804f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
480508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
480608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
480708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
480808ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
4809e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
4810e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
4811d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
4812d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4813d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4814f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
4815d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
4816d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4817f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
4818d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
4819d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
4820d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4821d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
4822d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4823f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
4824f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
4825f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
4826d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
4827ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
4828ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
4829d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4830d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4831d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4832f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
4833f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
4834f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
4835f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
4836f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
4837f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
4838f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
4839f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
4840d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4841d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
4842d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
4843d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
4844d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4845d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
4846d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
4847d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
48487a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
48497a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa                new VCardComposer(context, VCardConfig.VCARD_TYPE_DEFAULT, false);
4850d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
4851d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4852f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
48537a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        if (!composer.init(selection, selectionArgs)) {
48547a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa            Log.w(TAG, "Failed to init VCardComposer");
4855d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
48567a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        }
4857d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4858d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
4859d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
4860d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
4861d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4862d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4863d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
4864d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4865b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
48664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
48674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
4868415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
4869415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
4870415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
4871a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
48724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
4873b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
4874be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
48752d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
4876b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
4877b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
4878b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
4879f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
488042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
4881f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
4882f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
4883f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                return "image/png";
4884b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
4885be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
4886b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
4887b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
4888f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
4889f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
4890508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
4891b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
489248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
489348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
489448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
489548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
48969005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
48979005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
489848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
489948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
490048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
490148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
490248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
490348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
490448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
490548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
4906b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
4907b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
4908b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
4909b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
4910b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
4911b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
4912b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
4913b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
4914c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
4915c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
4916c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
4917c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
4918d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
4919d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
4920d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
4921d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
492261efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
492361efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
49244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
49254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
49267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
492709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
492809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
492909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
493009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
493109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
493209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
493309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
493409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
493509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
493609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
49378727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
49388727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
49398727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
494009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
494109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
494209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
494309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
494409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
494509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
494609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
494709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
494809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
494909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
495009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
495109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
495209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
495309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
495409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
495509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
495609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
495709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
495809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
495909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
496009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
496109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
496209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
496309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
496409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
496509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
496609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
496709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
496809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
496909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
497009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
497109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
497209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
497309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
497409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
497509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4976f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
4977f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4978f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
4979f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
4980f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4981f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4982f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4983f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
4984f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
498578fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.insertNameLookup(rawContactId, dataId, lookupType, name);
4986f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4987f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4988f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4989f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
4990d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
4991f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4992f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4993f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
49942d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
4995d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
4996d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
4997d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
4998d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
4999d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
5000d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
5001d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
5002e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
5003916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
5004916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
5005e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
5006e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
50075ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
5008c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
50097318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam);
5010c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
5011c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5012c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
50137318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam) {
50147318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), true);
50155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
50165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
50175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
50187318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov            boolean allowEmailMatch) {
501923061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        if (TextUtils.isEmpty(normalizedName)) {
502023061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // Effectively an empty IN clause - SQL syntax does not allow an actual empty list here
502123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("(0)");
502223061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        } else {
502323061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("(" +
502423061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
502523061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " FROM " + Tables.NAME_LOOKUP +
502623061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " WHERE " + NameLookupColumns.NORMALIZED_NAME +
502723061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " GLOB '");
502823061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // Should not use a "?" argument placeholder here, because
502923061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME.
503023061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append(normalizedName);
503123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
503223061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_COLLATION_KEY + ","
503323061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NICKNAME + ","
503423061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_SHORTHAND + ","
503523061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.ORGANIZATION + ","
503623061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_CONSONANTS);
503723061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            if (allowEmailMatch) {
503823061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
503923061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            }
504023061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("))");
504123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        }
5042ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
5043ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
50449a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50459a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean appendEmailBasedDataFilter(StringBuilder sb, String filter) {
50469a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (filter.indexOf('@') == -1) {
50479a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return false;
50489a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50499a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50509a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String address = mDbHelper.extractAddressFromEmailAddress(filter);
50519a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (TextUtils.isEmpty(address)) {
50529a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return false;
50539a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50549a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50559a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(DataColumns.MIMETYPE_ID + " IN (");
50569a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(mDbHelper.getMimeTypeIdForEmail());
50579a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(",");
50589a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(mDbHelper.getMimeTypeIdForIm());
50599a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(",");
50609a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(mDbHelper.getMimeTypeIdForSip());
50619a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(") AND " + Data.DATA1 + " LIKE(");
50629a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, address + '%');
50639a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(")");
50649a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return true;
50659a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
50669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50679a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean appendPhoneNumberBasedDataFilter(StringBuilder sb, String filter) {
50689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!isPhoneNumber(filter)) {
50699a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return false;
50709a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String number = PhoneNumberUtils.normalizeNumber(filter);
50739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(DataColumns.CONCRETE_ID + " IN " +
50749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                "(SELECT " + PhoneLookupColumns.DATA_ID
50759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                + " FROM " + Tables.PHONE_LOOKUP
50769a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
50779a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(number);
50789a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append("%'");
50799a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50809a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String numberE164 = PhoneNumberUtils.formatNumberToE164(number, mDbHelper.getCountryIso());
50819a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!TextUtils.isEmpty(numberE164)) {
50829a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
50839a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append(numberE164);
50849a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sb.append("%'");
50859a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50869a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(")");
50879a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50889a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String normalizedFilter = NameNormalizer.normalize(filter);
50899a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (TextUtils.isEmpty(normalizedFilter)) {
50909a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return true;
50919a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
50929a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
50939a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(" OR " + DataColumns.CONCRETE_RAW_CONTACT_ID + " IN " +
50949a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                "(SELECT " + NameLookupColumns.RAW_CONTACT_ID +
50959a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
50969a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
50979a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " GLOB '");
50989a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(normalizedFilter);
50999a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
51009a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                + CONTACT_LOOKUP_NAME_TYPES + "))");
51019a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return true;
51029a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
51039a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
51049a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean appendNameBasedRawContactFilter(StringBuilder sb, String filter) {
51059a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        String normalizedFilter = NameNormalizer.normalize(filter);
51069a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (TextUtils.isEmpty(normalizedFilter)) {
51079a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            return false;
51089a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
51099a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
51109a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(DataColumns.CONCRETE_RAW_CONTACT_ID + " IN " +
51119a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                "(SELECT " + NameLookupColumns.RAW_CONTACT_ID +
51129a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
51139a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
51149a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                " GLOB '");
51159a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        // Should not use a "?" argument placeholder here, because
51169a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME.
51179a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append(normalizedFilter);
51189a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
51199a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                + CONTACT_LOOKUP_NAME_TYPES + "))");
51209a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return true;
51219a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
51229a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
51239a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
51249a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
51259a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
51269a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
51279a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
51289a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
51299a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
51309a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
51319a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
51329a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
51339a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
51349a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
51359a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
51369a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
51379a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
51384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
51397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
51407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
51417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
51427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
51437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
51447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
51457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
51467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
51477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
5149f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
5150f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
51517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
51537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
51547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
51557a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
51567a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
51577a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
51587a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
51597a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
51607a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
51617a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
51627a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
51637a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
51647a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51657a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
51667a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51677a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
51687a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
51697a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
51707a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
51717a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
51727a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
51737a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
51747a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
51757a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51767a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
51777a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
51787a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
51797a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
51807a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
51817a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
51827a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
51837a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
51847a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51857a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
51867a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
51874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
51884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
51894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5190b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
5191b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
5192b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
5193b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
5194b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
51954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
51964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5197b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
5198b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5199b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5200caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
52015e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
52025e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
52035e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
52045e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
52055e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
52065e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
52075e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
52085e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
52095e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
52105e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
52115e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5212caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
5213caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
5214caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
52155f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
5216caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
5217caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
5218caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
5219caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
52206f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
5221caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
52226f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
5223caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
5224f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
522573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
522673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
522773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
522873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
5229bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
5230bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
5231bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
5232bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
523373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
523473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
523573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
523673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
523773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
5238627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
5239627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
5240627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
5241627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
524273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
524373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
524473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
5245627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
5246627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
5247627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
5248627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
5249627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
525073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
525173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
525273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
525373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
525473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
525573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
525673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
5257627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
5258b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
5259d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5260f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
5261f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
5262f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5263f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
5264f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5265f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5266f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5267f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5268f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5269f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
5270f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
5271f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5272f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5273f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5274f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
5275f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5276f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
5277f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
5278f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5279f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5280f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
5281f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
5282f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
5283f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
5284f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
5285f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5286f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5287f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
5288f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
5289f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
5290f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
5291f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5292f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5293f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
5294f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5295f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5296f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
5297f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
5298f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5299f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
5300f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
5301f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
5302f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
5303f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
5304f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5305f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5306f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5307f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
5308f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5309f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
5310f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5311f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5312f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5313f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
5314f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
5315f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
5316f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5317f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5318f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5319f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
5320f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
5321f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
5322f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
5323f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
5324f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5325f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5326f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
5327f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
53285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
53290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
53300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
53310dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
53320dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
53330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53340dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
53350dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
53360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
53370dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
5338bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
53390dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
53400dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
53410dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
53420dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
53430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
53440dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
53450dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
534649d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = mDbHelper.getWritableDatabase();
53470dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
53480dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
53490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
53500dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
53510dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
53520dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
53530dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
53540dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE,
53550dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
53560dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
53570dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
53580dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
53590dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
53600dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
53610dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
53620dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
53630dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
53640dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
53650dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
53660dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mContactAggregator.aggregateInTransaction(mDb);
53670dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
53680dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
53690dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
53700dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
53710dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
53720dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
53730dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
53740dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
53750dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
53760dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
53779a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
53789a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
53799a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
53809a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
53819a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
53829a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
53839a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
53849a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
53859a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
53864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
5387