ContactsProvider2.java revision 46abbb56764add30cb6e6506f55d8dededc88113
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;
2324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoroimport com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
2497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
2597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
2797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
2897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
2997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
3097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
3746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
3803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
3997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
4097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
4197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
4297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardComposer;
4397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardConfig;
4497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
4597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
4697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
4797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
48b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
49caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
505b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
51bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.Notification;
52bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.NotificationManager;
53bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.PendingIntent;
54c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
55568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
56568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
576ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
5835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
5967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
6067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
61627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
62bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.Intent;
63568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
65627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
6667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
67f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
68e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CrossProcessCursor;
694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
70e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CursorWindow;
71ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
72ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
7309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
7409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
7608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
79d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
80bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Binder;
816ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
82bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Handler;
83bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.HandlerThread;
84bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Message;
85ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
86bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Process;
87b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
8815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.os.StrictMode;
890dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
900e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
913d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
92508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
933de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
94b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
9597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
9697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
9797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
9897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
996d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Note;
10097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
10197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
10297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
10397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
10497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
105ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
1063de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1075b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1083de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
109d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
1103de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
111bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1123de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
11309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1143de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
11546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport android.provider.ContactsContract.DataUsageFeedback;
116916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1173de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
11882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
11997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
12097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
12197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
122a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
1239a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport android.telephony.TelephonyManager;
124a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
125c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
127108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.BufferedWriter;
128d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
129b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
130d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
131d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
132108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.OutputStreamWriter;
133108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.Writer;
13442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
13646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport java.util.Arrays;
1375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
13842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
139b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1400e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
142622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
143b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1440e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
145ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1515b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
152caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
153bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
154bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
155bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
15715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_INITIALIZE = 0;
15815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
15915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS = 2;
16015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
16115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
16215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
16305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
16405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
16505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
16605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
167619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
172b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1733d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1743d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
176b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
177b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
17851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
1793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1800dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
1810dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
1820dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
1830e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1840e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
185a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
187dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov    private static final String TIMES_CONTACTED_SORT_COLUMN = "times_contacted_sort";
1885e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
189d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
190dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov            + TIMES_CONTACTED_SORT_COLUMN + " DESC, "
1919b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
192d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
193d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
194d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
195d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1966e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
1979b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
1989b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1999b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
2009b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2016e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2029b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
2039b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2049b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
2059b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
206de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
207de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2093716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
212d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
213d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
216a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
221a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
222f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
22342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private static final int CONTACTS_AS_MULTI_VCARD = 1011;
2242149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_DATA = 1012;
2252149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_DATA = 1013;
226a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_ENTITIES = 1014;
227a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ENTITIES = 1015;
228a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1016;
2294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2315ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
23346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
2344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2356bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2366bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
237ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
23848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
23948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
24048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
24148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
24248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
24348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
24448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
24548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
246a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2476bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2486bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
249b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
250b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
251b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
25282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
25382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
25531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
25631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
257eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
258eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
259ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
260ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
261ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
262ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
26335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
264b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
26535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
266c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
267c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
268c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2691b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2701b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2711b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2721b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2731b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
27446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
27546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
27609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
27709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
278d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
279d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
280d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
2817a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
2827a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
28324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
28424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
28524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
28624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
28724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
28824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
28924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
29024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
29124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
29224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
29346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
29446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
295dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
296dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
297dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
298dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
299dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
300dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE
301dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
302dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
303dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
304dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
305dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
306dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
307dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
308dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND "
309dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + Groups.AUTO_ADD + " != 0";
310dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
311dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
312dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
313dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
314dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
315dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
316dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
317dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
318dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
319dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
320dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
321dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
322e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    public class AddressBookCursor extends CursorWrapper implements CrossProcessCursor {
323e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final CrossProcessCursor mCursor;
324e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final Bundle mBundle;
325e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
326e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public AddressBookCursor(CrossProcessCursor cursor, String[] titles, int[] counts) {
327e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            super(cursor);
328e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor = cursor;
329e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle = new Bundle();
330e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
331e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
332e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
333e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
334e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
335e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public Bundle getExtras() {
336e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mBundle;
337e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
338e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
339e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
340e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public void fillWindow(int pos, CursorWindow window) {
341e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor.fillWindow(pos, window);
342e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
343e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
344e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
345e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public CursorWindow getWindow() {
346e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.getWindow();
347e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
348e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
349e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
350e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public boolean onMove(int oldPosition, int newPosition) {
351e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.onMove(oldPosition, newPosition);
352e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
353e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    }
354e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
355d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
356f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
357f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
358f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
35967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
36067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
3616cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
3623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
363f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
364ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
365ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
366d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
36767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
368d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
369ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
371f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
37219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
37319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
37419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
375ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
376ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
377ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
37819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
37919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
38019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
381ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
382ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
38319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
38419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
385c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
386caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
38771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
38871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
38971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
39071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
39171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
39271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
39371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
39471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
39571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
39671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
39771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
39871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
39971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
40071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
401a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
402a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
403a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
404a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
405a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
406a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
407a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
408a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
409a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
410a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
411a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
412a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
413c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
414c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
415c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
416c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
417c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
418c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
419c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
420c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
421c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Within the starred/unstarred groups - three buckets: very recently contacted, then fairly
422c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
42346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
42446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
425c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
426c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
42746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        "(CASE WHEN " + Contacts.STARRED + "=1 THEN 0 ELSE 1 END), "
42846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + "(CASE WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_CURRENT
42946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
43046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + " WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_RECENT
43146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
43246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
43346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
43446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
43546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
43646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC";
43746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
43846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
43946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
440c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
441916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
442916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
443916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
444916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
44592ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
446916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
447f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
448f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
449f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
450f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
451f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
452f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
453f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
454f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
455f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
456f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
457f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
458f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
459f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
460f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
461f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
462916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
463f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
464f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
465f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
466f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
467f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
468f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
469f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
470f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
471f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
472f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
473f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
4743d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
4753d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
476f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
477f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
478f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
479f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
480f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
481cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
482f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
483f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
484f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
485f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
486f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
487f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
488f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
489f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
490f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
491f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
492f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
493f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
494f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
495f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
496f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
497f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
498f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
499f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
500f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
501f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
50203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
503f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
504f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
505f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
506f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
507f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
508f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
509f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
510f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
511f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
512f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
513f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
514f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
515f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
516f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
517f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
518f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
519f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
520f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
521f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
522f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
523f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
524f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
525f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
532f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
533f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
576038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
581e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
58624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
591916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
596916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5975e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, Contacts.TIMES_CONTACTED)
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
608f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
614ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
63224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
637a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.IS_RESTRICTED)
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
64524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
651a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.IS_RESTRICTED)
66024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
67524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
68724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
698f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
701f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
7043d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
7053d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
709f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
710f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
7122530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
715ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
716f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
717f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
733c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
740ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ")")
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Contacts.HAS_PHONE_NUMBER + ")")
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
757373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
765eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
80382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
817f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
821f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
823f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
824f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
8251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
826f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
827f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
828f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
829f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
830f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
831f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
832f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
833f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
834d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
835f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
838f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
839f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
840f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
841f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
844778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
845778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
846f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
8477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
8489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
8499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
8509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
8519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
8529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
8539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
8542526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
8552526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
856bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
857bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
858bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
859bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
86051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
86103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
86203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
86303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
86403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
86503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
8669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
8679a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
8689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
869f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
8701129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
8711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
8722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
8732526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
874f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
875f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
87646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
87746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
87846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
87946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
88046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
88146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
8824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
8834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
884a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
885d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
886d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
887a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
888a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
8893653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
8903653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
8912d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
8922d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
893a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
894c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
8955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
8965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
8972149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
8985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
8992149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
9002149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
901a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
902a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
903a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
904a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
905f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
90642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
90742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
9085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
909ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
910ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
9115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
9123653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
9135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
9145ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
9155ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
91646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
91746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
91846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
919b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
9204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
9214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
922ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
92348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
9245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
925ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
9264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
92748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
9281dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
9295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
9305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
9314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
932ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
93348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
93446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
93546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
9361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
937ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
938ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
939ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
940ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
94135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
942b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
943b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
94435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
945a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
946b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
947b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
948b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
949b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
9504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
951eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
952eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
95382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
95482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
9551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
956c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
957c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
958c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
959c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
9602d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
961c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
962c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
9631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
9641b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
9651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
9661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
9671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
9681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
9691b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
9701b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
97109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
97209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
973d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
974d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
975d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
9767a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
9777a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
97824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
97924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
98024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
98124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
98224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
98324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
98424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
98524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
98624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
98724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
98824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
98924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
99024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
99146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
99246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
99346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
99446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
99546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
99646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
99746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
99846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
99919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
100019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1001d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1002d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1003d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1004d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1005d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1006d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1007d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1008d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1009d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
10104458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
10114458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1012d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
10133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
1014ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1015ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1016ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1017e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1018ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1019ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1020ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1021ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1022ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1023a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1024e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1025e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1026e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1027e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1028e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
102924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
103024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Cached information about the contact ID and raw contact IDs that make up the user's
103124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile entry.
103224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
103324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static class ProfileIdCache {
103424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean inited;
103524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        long profileContactId;
103624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileRawContactIds = Sets.newHashSet();
103724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileDataIds = Sets.newHashSet();
103824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
103924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        /**
104024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * Initializes the cache of profile contact and raw contact IDs.  Does nothing if
104124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * the cache is already initialized (unless forceRefresh is set to true).
104224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param db The contacts database.
104324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param forceRefresh Whether to force re-initialization of the cache.
104424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         */
104524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        private void init(SQLiteDatabase db, boolean forceRefresh) {
104624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (!inited || forceRefresh) {
104724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileContactId = 0;
104824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileRawContactIds.clear();
104924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileDataIds.clear();
105024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                Cursor c = db.rawQuery("SELECT " +
105124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_CONTACT_ID + "," +
105224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "," +
105324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        DataColumns.CONCRETE_ID +
105424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " FROM " + Tables.RAW_CONTACTS + " JOIN " + Tables.ACCOUNTS + " ON " +
105524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" +
105624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        AccountsColumns.PROFILE_RAW_CONTACT_ID +
105724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " JOIN " + Tables.DATA + " ON " +
105824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_RAW_CONTACT_ID,
105924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        null);
106024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                try {
106124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    while (c.moveToNext()) {
106224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (profileContactId == 0) {
106324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            profileContactId = c.getLong(0);
106424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
106524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactIds.add(c.getLong(1));
106624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileDataIds.add(c.getLong(2));
106724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
106824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                } finally {
106924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    c.close();
107024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
107124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
107224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
107324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
107424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
107524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private ProfileIdCache mProfileIdCache;
107624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
10773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1078b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
107931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
10804097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1081f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1082315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1083622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1084622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
108572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
1086622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1087f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1088a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1089d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1090f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1091a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
109220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
109373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
109420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
109509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
10963826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
109709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
109815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
109915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
110015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1101bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
110273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1103d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private TransactionContext mTransactionContext = new TransactionContext();
1104de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
11051a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
11061a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
110781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
110881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
11094cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
11103826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1111d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1112bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1113bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1114bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
11154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
11164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1117de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1118ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1119ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1120ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1121ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1122ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1123ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1124ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
112535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1126ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
112715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
112815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
112915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
113024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache = new ProfileIdCache();
1131b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
113272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1133a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
113465ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1135bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
113615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
113715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
113872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1139bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1140bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1141bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1142bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1143bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1144bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1145bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1146bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1147bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
11482a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
114915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1150bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1151bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1152bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1153bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
115405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1155bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
115615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
11573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
115849d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
11594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
11604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1161767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
116251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
116351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
116404b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
116515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
116615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
11674cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
116804b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
11694cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
11704cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
117151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
1172cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
11735b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
117415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
11755b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1176f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
11775b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1178bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
1179bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1180bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE,
11816d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForEmail(context, mDbHelper, mContactAggregator));
1182bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
11836d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForIm(context, mDbHelper, mContactAggregator));
1184bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE,
11856d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForOrganization(context, mDbHelper, mContactAggregator));
1186bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE,
11876d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoneNumber(context, mDbHelper, mContactAggregator));
1188bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
11896d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNickname(context, mDbHelper, mContactAggregator));
1190bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
11916d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredName(context, mDbHelper, mContactAggregator,
1192bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
1193bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
11946d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredPostal(context, mDbHelper, mContactAggregator,
1195bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
1196bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE,
11976d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForGroupMembership(context, mDbHelper, mContactAggregator,
1198bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
1199bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE,
12006d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoto(context, mDbHelper, mContactAggregator));
12016d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov        mDataRowHandlers.put(Note.CONTENT_ITEM_TYPE,
12026d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNote(context, mDbHelper, mContactAggregator));
1203bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1204bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1205bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1206bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1207bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1208bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1209bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1210bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1211bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1212bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1213bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1214bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1215bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1216bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1217bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1218bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1219bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1220bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1221bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
122215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
122315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
122415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
122515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
122615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
122715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
122815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
122915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1230bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
123115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
123215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1233bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1234bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1235bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1236bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1237bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1238bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1239bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1240bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1241bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1242bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1243bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1244bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
124515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
124615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
124715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
124815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
124915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
125015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
125115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
1252bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
1253bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1254bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1255bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1256bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1257bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1258bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1259bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1260bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1261bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1262bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1263fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1264fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1265fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1266fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1267fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1268bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1269bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1270bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1271bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1272bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1273bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1274bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
127505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
127605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
127705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
127805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
127905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1280bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1281bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1282bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1283bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1284bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1285bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1286bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1287bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1288bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1289bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1290bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1291bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
12924cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
12934cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
129453fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
12953826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
12963826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
12974f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
12984f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
12994f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1300fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
13014cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
130251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
130351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
130451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
130551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
130651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
130751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
130851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
130951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1310bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1311f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1312f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1313f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1314f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1315f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1316f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
131751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
131851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
131951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
132051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
132151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
132251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
132351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
132451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
132551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
1326bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, currentLocale);
1327bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1328bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1329bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
133051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1331fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1332fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1333fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1334fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1335fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1336fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1337fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1338fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
1339fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1340fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
1341fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1342fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1343fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
1344fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1345fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
1346fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1347fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1348fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1349fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1350fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
135105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
135205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
135305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
135405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1355bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1356bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
135751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
135851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
13593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
13603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
13613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
13623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
13633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
13643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
13653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mContactsAccountCount == 0
136649d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                && DatabaseUtils.queryNumEntries(mDbHelper.getReadableDatabase(),
136749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                        Tables.CONTACTS, null) == 0) {
13683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
13693826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
13703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
13713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
13723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
13733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
137431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1375de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1376b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1377b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
137831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
137931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1380013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1381013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1382013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1383013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
13845df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
13855df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
13865df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
13875df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
13885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1389ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
139072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
139172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
139272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
139372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
13945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
13955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
13965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
13975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
13983d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
1399b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1400b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
14013d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
14023d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1403568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1404568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1405568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1406568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1407568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1408bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1409568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1410bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1411bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1412bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1413568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1414bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1415bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, mCurrentLocale);
1416bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1417568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1418bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1419bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1420bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1421bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1422bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1423bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1424568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1425568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1426bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1427bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1428bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1429bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1430bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1431bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1432bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1433bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1434b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
1435b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1436b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1437bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1438bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1439bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1440bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1441bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1442bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1443bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1444bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1445bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1446bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1447bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1448bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1449bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1450bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1451bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1452bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1453bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1454bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1455bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1456bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1457bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1458bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1459bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1460bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1461bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1462bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1463bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1464bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1465bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
14663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
14673d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
14683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1469568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
14700e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
14713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
14723d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1473bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1474bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1475bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1476bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1477bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1478bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
14793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
14803d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
14813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1482bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1483bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
14843d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
14853d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1486a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1487a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1488a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1489a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1490b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
14913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1492a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1493a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1494568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
149515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1496568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1497568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1498568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1499568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1500568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
150115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
150215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
150315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
150415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
150515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
150615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
150715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
150815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
150915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
151015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
151115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1512ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1513568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1514568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1515568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1516568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1517568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
151815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1519568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1520568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1521568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1522568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1523568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
152415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
1525bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
1526bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
1527bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
1528bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
1529bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
1530bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
1531bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
1532bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1533bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
1534bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
1535bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
1536bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
1537bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1538bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
153915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1540568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1541568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1542568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1543568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1544568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
154515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1546568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1547568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1548568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1549568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1550568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1551568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
155215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1553568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1554568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1555568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
15564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
15577b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
15587b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
15597b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        return super.bulkInsert(uri, values);
15607b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
15617b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
15627b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
1563285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1564bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1565b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1566b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1567285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
15681ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1569d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1570b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1571b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1572285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1573285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1574285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
15751129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1576bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1577b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1578b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1579285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1580b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
1581bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
15821a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
15831a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1584b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
15851a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
15863826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
1587bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
1588bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
15893826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
15903826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
15913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
15923826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
1593b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1594b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1595bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
1596bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleContacts = mTransactionContext.getStaleSearchIndexContactIds();
1597bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleRawContacts = mTransactionContext.getStaleSearchIndexRawContactIds();
1598bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
1599bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
1600bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mTransactionContext.clearSearchIndexUpdates();
1601bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
1602bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
1603bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
1604b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1605bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1606b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1607b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
16081129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
160924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Determine whether we need to refresh the profile ID cache.
161024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean profileCacheRefreshNeeded = false;
161124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1612d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (long rawContactId : mTransactionContext.getInsertedRawContactIds()) {
16138ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov            mDbHelper.updateRawContactDisplayName(mDb, rawContactId);
1614bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.onRawContactInsert(mTransactionContext, mDb, rawContactId);
1615285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1616b5a4add17815167d20a90645779df34cdf45280dFred Quintana
161724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Map<Long, Account> insertedProfileRawContactAccountMap =
161824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mTransactionContext.getInsertedProfileRawContactIds();
161924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (!insertedProfileRawContactAccountMap.isEmpty()) {
162024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            for (long profileRawContactId : insertedProfileRawContactAccountMap.keySet()) {
162124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mDbHelper.updateRawContactDisplayName(mDb, profileRawContactId);
162224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mContactAggregator.onProfileRawContactInsert(mTransactionContext, mDb,
162324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactId,
162424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        insertedProfileRawContactAccountMap.get(profileRawContactId));
162524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
162624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = true;
162724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
162824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1629d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> dirtyRawContacts = mTransactionContext.getDirtyRawContactIds();
1630d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
1631a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1632a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1633d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
1634a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1635a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
163624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
163724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
163824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, dirtyRawContacts);
1639a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1640a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1641d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> updatedRawContacts = mTransactionContext.getUpdatedRawContactIds();
1642d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
1643a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1644a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1645d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
1646a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1647a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
164824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
164924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
165024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, updatedRawContacts);
1651b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1652b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1653d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (Map.Entry<Long, Object> entry : mTransactionContext.getUpdatedSyncStates()) {
1654b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
16559d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
16569d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
16579d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
16589d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
1659b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1660b5a4add17815167d20a90645779df34cdf45280dFred Quintana
166124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (profileCacheRefreshNeeded) {
166224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Force the profile ID cache to refresh.
166324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mProfileIdCache.init(mDb, true);
166424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
166524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1666d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1667b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1668b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1669a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1670a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1671a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1672a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1673d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
1674b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1675a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1676b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1677a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1678a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1679285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1680285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
168124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
168224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given contact ID represents the user's personal profile - if it is, calls
168324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * a permission check (for writing the profile if forWrite is true, for reading the profile
168424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * otherwise).  If the contact ID is not the user's profile, no check is executed.
168524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param contactId The contact ID to be checked.
168624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
168724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
168824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermissionForContact(long contactId, boolean forWrite) {
168924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache.init(mDb, false);
169024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileContactId == contactId) {
169124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
169224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
169324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
169424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
169524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
169624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given raw contact ID is a member of the user's personal profile - if it
169724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * is, calls a permission check (for writing the profile if forWrite is true, for reading the
169824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the raw contact ID is not in the user's profile, no check is
169924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * executed.
170024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param rawContactId The raw contact ID to be checked.
170124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
170224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
170324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermissionForRawContact(long rawContactId, boolean forWrite) {
170424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache.init(mDb, false);
170524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileRawContactIds.contains(rawContactId)) {
170624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
170724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
170824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
170924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
171024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
171124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given data ID is a member of the user's personal profile - if it is,
171224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * calls a permission check (for writing the profile if forWrite is true, for reading the
171324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the data ID is not in the user's profile, no check is executed.
171424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param dataId The data ID to be checked.
171524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
171624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
171724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermissionForData(long dataId, boolean forWrite) {
171824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache.init(mDb, false);
171924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileDataIds.contains(dataId)) {
172024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
172124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
172224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
172324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
172424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
172524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Performs a permission check for WRITE_PROFILE or READ_PROFILE (depending on the parameter).
172624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * If the permission check fails, this will throw a SecurityException.
172724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
172824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
172924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermission(boolean forWrite) {
173024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String profilePermission = forWrite
173124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? "android.permission.WRITE_PROFILE"
173224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : "android.permission.READ_PROFILE";
173324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        getContext().enforceCallingOrSelfPermission(profilePermission, null);
173424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
173524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1736285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1737cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
173881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
173981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
174081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
174181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
174281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
174381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
174481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
1745cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
1746568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
174751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
17483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
17493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
17503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
17513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
175251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
175351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1754f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
17553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
17563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
17576d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
17586d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                    getContext(), mDbHelper, mContactAggregator, mimeType);
17593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
17603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
17613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
17623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
17633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
17644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1765de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
1766bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
17671129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
1768b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1769f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1770f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
1771f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
1772f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1773a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
1774a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
177535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1776a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
177735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
1778b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
177935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
178035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1781d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
1782d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
17836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
17846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
17856bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
178624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
178724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
178824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
178924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
179024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
17915ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
179224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, false);
1793f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1794a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1795a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1796a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
17975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
17985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
1799f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1800f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1801a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1802a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1803a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
180424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
180524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(true);
180624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, true);
180724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
180824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
180924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
181024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1811a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
1812f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1813f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1814a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1815a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1816a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1817ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1818f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
1819f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1820ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1821ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1822ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1823eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
18245aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
182543880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
1826eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
1827eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
1828eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
182982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
183082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
18311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
18321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
18331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1834a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
183581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
1836f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
1837a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1838a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
18397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
18407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
18417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
18427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1843de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
1844a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1845a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1846a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1847e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
1848e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
1849e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
1850e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
1851e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
1852e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
1853e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
1854e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
1855e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
1856e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
1857e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
1858e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
1859e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
18607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
1861e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
1862f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
1863f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
1864e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
1865f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1866f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1867f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
1868e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
1869e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
1870e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1871e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
1872e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
1873fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
1874fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
1875e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
1876e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1877e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
1878e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
1879e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
1880e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
1881e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1882e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
1883e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
1884e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
1885e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
1886e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
1887fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
1888fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
1889e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
1890e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
1891e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
1892f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
1893f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
1894e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
1895f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
1896f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
1897e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
1898e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
1899f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
1900f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1901e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
1902f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
1903f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
1904f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
1905f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
1906035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1907f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1908e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
19097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
19107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
1912d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
19136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
19146bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
19156bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
19166bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
1917d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
1918de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
19196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
19206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
19216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
192224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
1923a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1924f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
1925f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
1926dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
192724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forProfile Whether this raw contact is being inserted into the user's profile.
1928a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1929a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
193024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter,
193124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            boolean forProfile) {
1932f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
1933f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
1934f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
1935f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1936e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
19377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19383d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
19393d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
1940f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
19413d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
19423d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1943f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
1944f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
194524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
194624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Profile raw contacts should never be aggregated by the aggregator; they are always
194724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // aggregated under a single profile contact.
194824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            aggregationMode = RawContacts.AGGREGATION_MODE_DISABLED;
194924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
1950f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
1951f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
1952f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
1953285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
195424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
195524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of the user profile Contact (or association with the existing one)
195624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // at the end of the transaction.
195724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mTransactionContext.profileRawContactInserted(rawContactId, account);
195824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else {
195924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of a Contact based on this RawContact at the end of transaction
196024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mTransactionContext.rawContactInserted(rawContactId, account);
196124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
1962f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1963dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
1964dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
1965dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
1966dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
1967dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
1968dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1969dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1970dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
19713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
1972023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
1973a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1974a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1975dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
1976dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
1977dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
1978dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
1979dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
1980dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1981dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1982dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1983dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
1984dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
1985dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
1986dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
1987dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
1988dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
1989dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
1990dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
1991dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1992dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
1993dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
1994dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
1995dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1996dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1997dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1998dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
1999dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2000dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2001dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2002dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2003dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2004dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2005dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2006dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2007dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2008dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2009dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2010dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2011dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2012dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2013dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2014dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2015dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2016dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2017dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2018dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2019dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2020dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2021dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2022dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2023dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2024dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2025dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2026dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2027a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2028a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2029a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2030a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2031a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2032a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2033f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2034a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2035de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2036de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
203767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2038de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
203920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
204024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // If the data being inserted belongs to the user's profile entry, check for the
204124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // WRITE_PROFILE permission before proceeding.
204224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForRawContact(rawContactId, true);
204324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2044de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2045de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2046de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2047b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2048de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2049de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2050508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2051de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2052de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2053de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2054de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2055de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
20564097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2057b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2058de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2059a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2060a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2061d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        id = rowHandler.insert(mDb, mTransactionContext, rawContactId, mValues);
2062f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2063d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.markRawContactDirty(rawContactId);
2064a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2065d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactUpdated(rawContactId);
2066a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
20674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
20684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2069ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
20708ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov        mDbHelper.updateRawContactDisplayName(db, rawContactId);
2071d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2072d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
20739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
207420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
207520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2076f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
207720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
207820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2079de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2080de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
2081f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS,
2082f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2083de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2084de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
2085f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
208624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
208724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for write profile permission if the data belongs to the profile.
208824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, true);
208924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2090f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
2091a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2092d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                count += rowHandler.delete(mDb, mTransactionContext, c);
2093f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
2094d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                    mTransactionContext.markRawContactDirty(rawContactId);
209588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
209620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
209720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2098de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
209920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
210020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
210120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
210220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
210320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
210488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
210588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
210688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
210720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2108f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
210988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
211088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
21114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
2112f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
21134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2114f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
211520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
211620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
211720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
211820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
211920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2120f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
212120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
212220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
212320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
212420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
212520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
212620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
212720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
212820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
212920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
21307a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
213120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
213220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
213320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
213424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Check for write profile permission if the data belongs to the profile.
213524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
213624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermissionForRawContact(rawContactId, true);
213724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2138a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2139d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return rowHandler.delete(mDb, mTransactionContext, c);
214020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
214120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
214220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
214320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
214420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
214520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2146ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2147ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2148f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2149f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2150f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2151f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2152e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
2153ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2154ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2155f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
215667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2157f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
215867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2159f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2160ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2161dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
2162dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
2163dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
2164dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2165f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2166f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
216773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
216873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2169f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2170ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2171dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
2172dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
2173dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
2174dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
2175dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
2176dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
2177dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
2178dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
2179dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2180dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
2181dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
2182dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
2183dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2184dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
2185dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
2186dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
2187892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
2188892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
2189892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
2190892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
2191892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
2192d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mTransactionContext.markRawContactDirty(rawContactId);
2193892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
2194dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2195892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
2196892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
2197dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2198dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2199dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2200f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
22011a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2202ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2203ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2204ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2205ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2206ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
22075aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2208e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
22095aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
22101a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
22111a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2212e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
22131a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2214e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2215e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2216e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2217ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
221882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
22191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
222082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
222182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
22220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
22234dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
22244dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
22250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
222682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
22274dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
22284dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
22294dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
22304dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
22311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
22321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2233dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2234dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
223582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2236f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
22372526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
2238dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2239dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2240dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
22412526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
22422526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
22431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2244dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2245dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
22460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
22470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
22480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
22490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2250dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2251dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2252dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
22532a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mDbHelper.getMimeTypeIdForIm());
2254dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
22552a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mDbHelper.getMimeTypeIdForEmail());
2256f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2257f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2258f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2259f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2260f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2261f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2262f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
22632526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
22642526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
22652526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
22662526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
22672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
22682526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
22692526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
22702526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
2271dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
22722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
22732526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2274dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
22752526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
22762526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
2277dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
22782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
22792526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
22802526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
22812526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
22822526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
22832526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
2284dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
22852526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
22862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2287dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2288dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
22891f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
229082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
22912526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
22922526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
2293dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
229470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
2295f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
229670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
22971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
22981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2299de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
23002526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
23014394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
23021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
230367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
23045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2305e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
23061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
23071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
23081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
23091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
23101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
231131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
231231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
231331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
23141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
23151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
231682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2317a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2318a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2319a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2320a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2321a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2322a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2323a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
232482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2325a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2326a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
232782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
232882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
232982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
233082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
233182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2332a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
233382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
233482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
2335aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
2336aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
23371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2338a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2339a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2340a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2341e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
23420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
234382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
234482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
23450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
23460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
23470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
23480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
23490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
23500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
23510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
23520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
23530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
23540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
23550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
23560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2357a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
235878fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteStatusUpdate(dataId);
235982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
236082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
236178fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.replaceStatusUpdate(dataId, timestamp, status, resPackage, iconResource,
236278fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
2363a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
236478fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.insertStatusUpdate(dataId, status, resPackage, iconResource,
236578fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
2366e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2367e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2368bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2369a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2370f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
2371a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2372a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2373a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
23741f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
23751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
23764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2377de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2378bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2379b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2380b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2381b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2382f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2383f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2384508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2385508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
238635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2387b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
238835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2389b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2390b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2391b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2392b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2393b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2394b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2395cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2396cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2397cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2398cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2399cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2400d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2401d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2402dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
24036bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
24046bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
24059fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
24062e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
24072e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
24082e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
2409fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2410fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
24112e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
24122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
24132e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
2414dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
24152e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
24162e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
24179fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
24189fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
24199fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
24209fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
24219fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
24229fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
2423a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
24249fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
24259fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
24269fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
24279fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
24289fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
24299fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
24309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
24319fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
243260de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
24339fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
24349fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
24359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
24369fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
24379fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
24389fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
2439dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
24409fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
24419fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
24429fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
24439fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
24449fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
24459fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
24469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
24479fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
24489fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
24499fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
24502971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
24512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
2452fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
2453fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
2454e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
24552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
24562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
24572971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2458fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
2459fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
2460fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
24612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
24622971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
24632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
24642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
24652971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
24662971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
24672971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
24685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
24692971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2470fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
2471fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
2472508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2473508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
247420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2475f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2476944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2477f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
247820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
247920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
248048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
248148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
248248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
248348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2484508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2485f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
24864da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
24874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
2488ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2489ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2490ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2491f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
24925aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
24932971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
24942971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
24952971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
24962971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
24972971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2498e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
24992971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
25002971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
25015aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
25022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
25032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
25042971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
25052971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
250681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2507f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
250881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
25092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2510508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2511508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2512eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
251343880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2514e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
2515eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2516eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
251782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
25180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
25191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
25201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
252181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
252281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
25233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
252481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2525508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
25264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
25274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
25281c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
2529ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2530b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
253194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2532de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
253394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
253494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
253594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
253694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2537f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2538de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
253994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
254094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
254194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2542f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2543de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
254494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
254594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
25461a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
254794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
254894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
254994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
25505aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2551e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
25521a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
2553e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2554e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2555e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2556dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
255724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForContact(contactId, true);
255896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
2559cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
256096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
256196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
2562cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2563cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2564cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2565dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
2566cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2567cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2568cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2569cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2570cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
25713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
25723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2573cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2574cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2575cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2576fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
257724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForRawContact(rawContactId, true);
25783389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
25793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
25803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2581f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
258214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2583fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
2584fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
2585fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
258633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2587b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2588dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
258933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
259033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
259133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
25920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
25939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
25949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
25959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
25969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
25979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
25989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
25999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
26009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
26010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
26020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2603dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
260481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
260581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2606cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2607cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2608cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2609cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2610cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2611cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2612dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
2613cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2614cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
26154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2616de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2617de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2618bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2619b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2620b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2621b5a4add17815167d20a90645779df34cdf45280dFred Quintana
262235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
262300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
262400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2625b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2626b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
26271129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
2628d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.syncStateUpdated(rowId, data);
2629b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2630b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2631b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2632f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2633f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
263400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
263535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2636b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2637b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2638b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2639b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2640b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2641b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2642b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2643b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2644b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2645b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2646b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
264735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2648d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2649dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
265000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
265100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
265200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2653d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2654dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
2655c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2656c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2657c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
265824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
265924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Restrict update to the user's profile.
266024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder profileSelection = new StringBuilder();
266124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileSelection.append(Contacts.IS_USER_PROFILE + "=1");
266224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (!TextUtils.isEmpty(selection)) {
266324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    profileSelection.append(" AND (").append(selection).append(")");
266424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
266524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                count = updateContactOptions(values, profileSelection.toString(), selectionArgs,
266624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        callerIsSyncAdapter);
266724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
266824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
266924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
26702e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
26712e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
26722e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
26732e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
26742e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
2675fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2676fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
26772e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
26782e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
26792e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
2680dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
26812e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
26822e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
26832e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
26847d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
26857d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
26867d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
26877d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
26887d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
26897d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
26907d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
26917d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
26927d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
26937d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
269420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2695944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2696f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
269781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2698f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
269981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
270020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
270120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2702c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
270348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
270448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
270548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
270648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2707f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
270881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2709f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
271081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
271100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
271200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
27137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
27145ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
27155ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
2716dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
27177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
27187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
27197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
27205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
272133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
27224529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
27234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
27244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
2725dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
2726dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
27274529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
27284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
2729dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
2730dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
27314529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
27327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
27337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
27347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2735ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
27365aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
2737f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
273881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2739f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
274081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2741ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2742ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2743ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2744ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2745ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
27464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
27474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
274873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
27495aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
27505aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
275181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2752f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
275381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2754ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2755ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2756ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2757127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
2758de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
2759b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2760b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2761b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2762eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
2763e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
2764e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
276543880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2766eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2767eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2768eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
27699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
27709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
27719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
27729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
27739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
277472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
2775bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
277672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
2777d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
2778d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
2779d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
278046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
278146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
278246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
278346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
278446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
278546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
278646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
278746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
278846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
278981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
279081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2791f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
279281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
279300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
279400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
279500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
27964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
27974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
27989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
27999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
28009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
28019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
28029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
28039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
28049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
28059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
28069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
28079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
28089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
28099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
28109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
28119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
28129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
28139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
28149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
28159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
28169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
28179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
28189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
28199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
28209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
28219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
28229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
28239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
28249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
28259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
28269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
28279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
28289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
28299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
28309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
28319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
28329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
28339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
28349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
28359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
28369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
28379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
28389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
28399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
28409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
28419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
28429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
28439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
28449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
28459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
28469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
28479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
28489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
28499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
28509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
28519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
2852aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
2853aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
28549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
28559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
28569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
28575aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
2858f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
285973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2860ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2861ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
286273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
2863f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
286473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
286573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
286673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
286773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
286873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
286973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
287073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
287173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2872ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
28731a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
28741a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
287594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
28766ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
28771129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
28786ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
2879e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
28806ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
28816ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
28826ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
28836ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
28846ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
28856ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
28866ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
288724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
28886ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
2889ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
28906ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
28916ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
28926ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
28936ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
28946ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
28956ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
28966ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
28976ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
289894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
289994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
290094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
2901b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
2902b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
2903e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
29041a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
29051a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2906e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2907e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2908e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2909e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2910dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
2911dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
29124529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
29134529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
29144529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
29154529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
291673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
291797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
291897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
291997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
292097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
292197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
29224529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
2923b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
292451bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
29254529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
29264529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
29274529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
29284529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
2929dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
29304529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
29314529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
29324529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
29334529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
29344529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
29354529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
29364529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
29374529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
29384529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
2939dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
2940dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
294124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
294224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Enforce profile permissions if the raw contact is in the user's profile.
294324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForRawContact(rawContactId, true);
294424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
294596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
294696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
294719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
294819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
294919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
2950ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
2951ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
295219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
295319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
295496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
295519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
295619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
295719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
2958ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
2959ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
296019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
296119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
296219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
296319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
296419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
296519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
296619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
2967f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
296896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
29695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
2970f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
2971f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
2972f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
2973f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
2974f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
2975f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
297669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
2977f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
2978f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
2979433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
2980dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
2981dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
2982dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
2983dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
29844529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
2985dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2986dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
2987dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
2988dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
2989dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
2990dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
2991dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
2992dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
2993dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
2994dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
2995dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2996dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2997dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2998dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
2999dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3000dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3001dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3002433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3003dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3004285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
30052b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3006285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3007f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3008f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3009f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3010f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3011f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
301278fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.resetNameVerifiedForOtherRawContacts(rawContactId);
3013f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3014f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3015f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
301619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
3017d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mTransactionContext.rawContactInserted(rawContactId,
3018d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        new Account(accountName, accountType));
301919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
30205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
30215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
302233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
302333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3024321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3025f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
302620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
302720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
302820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
30295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
303020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
303120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
303220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
303320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
303420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3035b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
303620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
303720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
303897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
303997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
304097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
304197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
304297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
3043653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
304420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3045653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3046653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
3047f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(uri, DataRowHandler.DataUpdateQuery.COLUMNS,
3048f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
3049653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3050653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
305124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check profile permission for the raw contact that owns each data record.
305224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = c.getLong(DataRowHandler.DataUpdateQuery.RAW_CONTACT_ID);
305324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, true);
305424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3055f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
305620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3057653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3058653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
305920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
306020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3061653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
306220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
306320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3064f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3065653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3066653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3067321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3068653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
3069f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
3070a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3071d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (rowHandler.update(mDb, mTransactionContext, values, c, callerIsSyncAdapter)) {
3072813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
3073813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
3074813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
3075a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
3076321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3077321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
30788c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
3079dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
30808c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3081b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
308224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                new String[] { Contacts._ID, Contacts.IS_USER_PROFILE }, selection,
30838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
30848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
30858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
30868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
308724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
308824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for profile write permission before updating a user's profile contact.
308924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean isProfile = cursor.getInt(1) == 1;
309024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (isProfile) {
309124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermission(true);
309224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
309324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3094dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
30958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
30968c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
30978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
30988c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
30998c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
31008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
31028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
31038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3104dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
3105dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
3106d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
310724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Check write permission if the contact is the user's profile.
310824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForContact(contactId, true);
310924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
31108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3111b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3112d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3113b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3114d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3115b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3116d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3117b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3118d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3119b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3120d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3121d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3122d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
31238c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3124d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3125d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3126d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
31278c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3128c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
31298c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3130c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3131c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
31324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
313397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
313497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
31358c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3136dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
3137dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
3138dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
3139dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
3140dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
3141dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
3142dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
3143dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3144dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
3145dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3146dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
3147dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
3148dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3149dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3150dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
31518c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
31528c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
31538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3154b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
31558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3156b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
31578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3158b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
31598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3160b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
31618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3162b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
31638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
31648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31659b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
31666e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
31679b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
31689b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
31699b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
31709b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
31719b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
31729b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
3173f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3174d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3175127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3176127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
31770c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
31780c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
317980c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
3180ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
3181ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
31820c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
31830c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
31840c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
31850c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
31860c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
31870c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3188b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3189127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
31900c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
31914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
31924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
31930c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
31944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
31954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
31960c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
31976bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
31986bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
31990c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
32000c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
32010c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
32020c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3203127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3204127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
32053389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
320669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
320769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
320869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
320969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
3210dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
3211bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId1);
3212bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId2);
3213127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3214127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3215127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3216127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3217b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3218b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
321970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
3220bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
32213826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
32223826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3223bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
3224f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
3225e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
3226627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
322749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
322870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
322970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
3230dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
3231743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
3232743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
3233743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
3234743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
3235e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
3236743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
3237743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
3238743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
3239743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
3240743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
324148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3242627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
3243743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
3244627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
3245627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
3246627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
324770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
324870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
324933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
3250e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
3251e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
3252e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
3253e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
3254e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3255e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
3256e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
3257e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
3258e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3259e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
3260e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
3261e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
3262e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
3263e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3264e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
3265e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3266e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
3267e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3268e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
3269e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3270e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
3271e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
3272e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
3273e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3274e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
3275e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
3276e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
3277d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
3278d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
3279d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
3280d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
32814458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
3282e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
3283e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
328433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
328533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
3286e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
328733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
328833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
328933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
329033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
329169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
329269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
329369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
329433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
329533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
329669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
329769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
329833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
329933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
330033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
330133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
330233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
330333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
330433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
330533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
330633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
3307bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                    mContactAggregator.updateAggregateData(mTransactionContext, contactId);
330833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
3309e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
3310bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                updateSearchIndexInTransaction();
331133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
331233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
3313e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
3314e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
3315e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
331670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
331770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
331870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
331970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
332073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
33213826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
33223826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
33233826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
33243826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
33253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
33263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3327afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
332870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3329619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
33303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
33313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
33323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
33333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
33343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
33353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
33363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
33373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
33383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
33393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
33403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
33413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
33423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
33433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
33443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
33453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
33463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
33473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
33483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
33493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
335072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
3351bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
3352d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3353d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3354619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3355627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
3356627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
3357dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
3358743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
3359743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
3360743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
3361627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
3362627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
3363dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
3364627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
3365627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3366627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3367627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
3368627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
3369627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
3370627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
3371627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
33724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
33734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
33744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
337515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
337615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
337715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
3378d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
3379385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
33803716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
33813716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1));
3382385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
33833716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
33843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
33853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                            Directory.DEFAULT));
3386d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
33873716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
33883716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
33893716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                            Directory.LOCAL_INVISIBLE));
3390d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3391d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3392d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
3393d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
3394a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
3395a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
3396d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3397d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3398d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
3399d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
3400d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
3401d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
3402d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
3403d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
3404d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3405d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
3406d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
3407d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
34082e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
34092e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
34102e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
34112e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
34122e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
34132e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
3414d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
341509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
341609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
341709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
341809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
341909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
3420332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
3421d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
34226ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
34236ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
34246ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
34256ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
34266ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
3427547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
3428547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
3429547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return wrapCursor(uri, cursor);
3430547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
3431547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return matrixCursorFromCursor(wrapCursor(uri, cursor));
3432547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
34333716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
34343716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
3435547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro    private Cursor wrapCursor(Uri uri, Cursor cursor) {
3436547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
3437547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
3438547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
3439547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return cursor;
3440547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
3441547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
34423716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
34433716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
34443716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
34453716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
34463716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
34473716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
34483716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
34493716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
34503716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
34513716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
34523716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
34533716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
34543716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
34553716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
34563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
34573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
34583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
34593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
3460547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        return new SnippetizingCursorWrapper(cursor, query, startMatch, endMatch, ellipsis,
3461547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro                maxTokens);
34626ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
34636ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
34646ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
34656ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
34666ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
34676ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
34686ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
34696ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
34706ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
34716ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
34726ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
34736ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
34746ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
34756ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
34766ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
34776ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
34786ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
34796ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
34806ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
34816ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
34826ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
34836ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
34846ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
3485332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
34866ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
3487d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3488d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3489d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
3490d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
3491d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
3492d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
3493d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
3494d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
3495d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
3496d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3497d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
3498d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
3499d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
3500d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
3501d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3502d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3503d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
3504d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
3505d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
3506d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
35074458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
35084458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
35094458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
351049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
351149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
35124458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
35134458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
35144458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
35154458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
35164458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
35174458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
35184458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
35194458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
35204458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
35214458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
35224458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
35234458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
35244458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
3525d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
35264458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
3527d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3528d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
35294458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
35304458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
3531d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3532d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
353372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
35344458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
35354458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
35364458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
353772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
353872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
3539d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    public Cursor queryLocal(Uri uri, String[] projection, String selection, String[] selectionArgs,
3540385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                String sortOrder, long directoryId) {
3541bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3542bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
3543bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
35440b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
3545b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
354635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3547d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
35481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
3549c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
3550c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3551619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
3552619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
3553a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
35544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
355535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3556b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
355735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
355835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3559d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3560763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
356124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean existingWhere = appendLocalDirectorySelectionIfNeeded(qb, directoryId);
356224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, Contacts.IS_USER_PROFILE, existingWhere);
356324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder);
3564619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
3565619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
3566619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3567d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
35684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
356924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForContact(contactId, false);
3570763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
35714da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
35724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
35736bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
35746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
35756bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
35765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
35775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
35785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
35795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
35805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
3581fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3582fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
35835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
3584a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
35855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
35865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
35875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
358824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermissionForContact(contactId, false);
35895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3590763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
3591a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3592a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3593a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3594a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
3595a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
35965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
35975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
35985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
35995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3600763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
36014da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
36024da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
36034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
36045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
36055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
36065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36072149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
36082149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
36092149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
36102149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
36112149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
36122149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
36132149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
36142149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
36152149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
36162149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
36172149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
361824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermissionForContact(contactId, false);
36192149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
36202149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
3621a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
3622a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3623a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3624a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
3625a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
36262149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
36272149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
36282149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
36292149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
36302149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
36312149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
36322149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
363324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
363424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForContact(contactId, false);
36352149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
363624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
36372149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
36382149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
36392149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
36402149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
3641f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
3642f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
364342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
364424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
364524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForContact(contactId, false);
3646763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
3647f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
36484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
364924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
36504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
3651f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
3652f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
3653f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
365442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
365542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
365642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
365742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
365842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
365942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
366042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
366142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
366242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
366342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
3664ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
3665916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
3666ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
3667916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
3668ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
36697ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
36707ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                        qb, uri, projection, filterParam, directoryId);
367124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, Contacts.IS_USER_PROFILE, false);
367224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder);
3673ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3674ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3675ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3676ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
3677ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
36784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3679ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3680d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
36814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
36824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3683e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
36845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
36854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
36864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
36874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3688763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3689ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
36905e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
36915e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
36925e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
3693dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    starredProjection =
3694dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
3695dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    frequentProjection =
3696dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
36975e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
369824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sStrequentStarredProjectionMap);
36995e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
37004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
37014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
370224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    qb.appendWhere(filterSql + " AND ");
3703d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
370424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
370524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                final String starredQuery = qb.buildQuery(starredProjection,
370624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
3707d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3708d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3709d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3710763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
371124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sStrequentFrequentProjectionMap);
37124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
371324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    qb.appendWhere(filterSql + " AND ");
3714d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
371524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
37165e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
3717d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3718d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
371924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts._ID, null, null, null);
3720d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3721d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3722d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3723d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
37244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
37254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3726d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3727d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3728d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3729d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3730d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3731d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3732ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3733763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3734b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
373571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
37364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3737b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3738b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3739b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3740b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
374124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
374224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
374324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
374424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
374524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
374624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
374724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
374824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
374924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
375024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
375124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Contacts.IS_USER_PROFILE + "=1");
375224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
375324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
375424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
375524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA: {
375624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
375724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
375824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
375924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
376024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
376124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
376224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA_ID: {
376324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
376424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
376524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
376624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Data._ID + "=? AND "
376724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
376824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
376924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
377024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
377124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
377224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
377324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // When reading as vCard always use restricted view
377424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
377524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
377624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
377724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
377824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
377924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3780a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
37814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
378282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37834da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
37844da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
37856bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
37866bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
378700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3788a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
37893653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
379082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
37924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
37933653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
37943653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
37953653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
37963653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
3797a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
3798a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
3799a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
3800a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
3801a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
3802a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
3803a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3804a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3805a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
3806a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
3807a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
3808a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
3809a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
3810a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3811a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
3812a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
3813a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
3814a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
3815a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
3816a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3817a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
3818a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
3819a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3820a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3821a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3822a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
3823a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
3824a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
3825a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
3826a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
3827a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
3828a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3829a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
3830a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
3831a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
3832a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
3833a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
3834a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3835a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
38364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
383782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
383889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
38392815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
38402815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
38412815f58f72f109790585931f601a63ddc02536a5Evan Millar
384248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
384382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
38444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
384548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
38464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
384748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
384848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
384948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3850ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
385146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
385246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
385346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
385446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
385546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
385646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
385789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3858ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
38594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
38604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3861a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
38625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
386345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
38645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
38655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
38665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
3867155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
3868155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
3869155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
3870155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
3871155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
3872155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
3873155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
38742352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
3875155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
38765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
387745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
38785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
38795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3880892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
3881892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
38825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
38835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
38845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
38855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
3886892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
3887892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
3888892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
3889892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
3890892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
389145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
389245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
389345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
389445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
389545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
389645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
389745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
38985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
38995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3900a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
3901ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
39025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3903a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
390446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
390546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
390646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
390746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
390846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
390946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
3910a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
3911ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3912ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3913ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
39144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
391582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
391689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
39174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
39184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
39194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
392048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
392182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
39224da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
39234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
39244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
392548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
392648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
392748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
39285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
392982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
393089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
39314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
393208768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
393308768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
393408768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
393508768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
39364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3937ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3938ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3939ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
39405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
394146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
394246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
394346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
394446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
394546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
394646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
394707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
39487d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
394907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
395007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
395107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
395207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
395307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
395407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
39555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
395607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
395707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
395807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
395907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
396007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
396107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
396207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
396307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
396407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
39652a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
39662a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(mDbHelper.getMimeTypeIdForEmail());
39672a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
396807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
396920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
3970155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
3971155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
3972155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
3973155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
3974155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(mDbHelper.getMimeTypeIdForEmail());
3975155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
3976155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
3977155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
3978155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
3979155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
3980155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
3981155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
39822352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
3983155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
39845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
39855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3986a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
39875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
39885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
3989a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
399046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
399146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
399246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
39937d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
39947d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
39957d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
3996a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
39975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
39985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
39995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4000ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
400182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
400289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
400389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4004ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4005ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4006ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
400748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
400882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
40094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
401048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
401148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
40124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
401348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
401448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
401548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
40165ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4017763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
401824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true);
40194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
40204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
40214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40225ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
40235ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
402424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, false);
4025763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
40264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
40274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
40284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
40294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
40304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40315ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
40325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
403382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
40344da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
40354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
403624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true);
403724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
403824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
403924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
404024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
404124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
404224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
404324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
404424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
404524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
404624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
404724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
404824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID: {
404924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
405024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = ContentUris.parseId(uri);
405124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
405224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
405324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
405424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
405524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
405624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
405724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
405824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
405924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
406024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
406124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
406224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
406324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
406424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + Data.RAW_CONTACT_ID + "=?");
406524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
406624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
406724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
406824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
406924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
407024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
407124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
407224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
407324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
407424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
4075e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4076e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4077e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
4078e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
407982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
408024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true);
4081e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4082e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4083e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
40844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
408524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = ContentUris.parseId(uri);
408624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForData(dataId, false);
408782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
40884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
40894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
4090a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
4091a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
4092a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
4093a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
40944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4095a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
4096a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
4097a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
4098892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
4099a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
4100a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4101e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
4102e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
4103e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        mDbHelper.getCurrentCountryIso());
4104892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
4105892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
4106892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
4107e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
4108e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
4109e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
4110e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
4111a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
4112a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
4113a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4114ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
4115b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4116ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
411789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4118ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4119ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4120ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4121ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
4122b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4123ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
41244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
41254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
4126ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4127ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4128ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4129ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
4130b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
4131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
413289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
413389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
4134ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4135ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4136ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4137b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
41380c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
4139b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
4140b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
4141b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
4142b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
414331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
4144d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
41452d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
41462d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
41472d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
41482d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
414931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
4150d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
4151d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
415231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
415331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
415431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
415531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
41565b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
41575b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
41585b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
41595b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
41605b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
41615b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
41625b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
41635b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
416476dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
41655b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
41665b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
41675b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
41685b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
41695b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
41705b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
41715b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
4172763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
41737581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
41747581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
41755b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
417631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
417731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4178eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
4179eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
4180eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
418189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4182e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4183e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
4184e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
4185b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
4186e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
418782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4188b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
4189e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4190e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
419182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4192b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
4193e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4194e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
4195e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4196eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
4197eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
4198eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
419982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
42000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
42015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
42025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
42035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
420482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
42050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
42064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
42074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
42085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
42095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
42105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
4211c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
4212174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
4213174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, uri, projection, limit);
4214c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4215c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4216c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
42172d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
4218174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
4219174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
4220174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
4221174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, projection, lookupKey, filter);
4222c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4223c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
42241b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
4225b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
42261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
42271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
42281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
42291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
4230b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
42311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
42321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
42331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
42341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
42351b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
4236b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
42371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
42381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
42391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
42401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
42411b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
4242b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
42431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
424471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
42451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
42461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
42471b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
424846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
4249a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
425046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
425146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
425246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
425346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
425446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
4255a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
42564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
42574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
425846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
425946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
426046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
426109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
426209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
426309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
426409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4265d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
4266d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4267d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4268d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4269d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4270d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4271d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
4272385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
4273d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4274d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4275385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
4276d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
4277d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4278d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4279d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
42807a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
42817a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
42827a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
42837a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
42844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
4285f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
4286c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
42874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
42884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
428909e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
42907f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
4291ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
4292ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
4293ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
4294ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
4295ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4296ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
42975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
42985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
43005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
43015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
4302038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
4303038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
4304038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
4305038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
43065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
43075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
43084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
43094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
43104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
43114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
43124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
43134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
431409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
431509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
431609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
431709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
431809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
431909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
432009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
432109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
432209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
432309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
432409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
432509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
432609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
432709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
432809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
432909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4330a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
4331a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
4332a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
4333a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
4334a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
4335a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
4336a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
4337a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
4338a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
4339a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
4340a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
4341a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
4342a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
4343a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
4344a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
4345a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
4346a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4347a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
4348a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
4349a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
4350a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
4351a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
4352a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
4353a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
4354a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4355a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4356a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
4357a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
4358a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
435909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4360bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
4361bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
4362bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
4363bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
4364ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4365bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4366bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
4367ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
4368ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4369bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
4370bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
4371bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
4372bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
437324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The first letter of the sort key column is what is used for the index headings, except
437424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // in the case of the user's profile, in which case it is empty.
437524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        public static final String SECTION_HEADING_TEMPLATE =
437624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "(CASE WHEN %1$s=1 THEN '' ELSE SUBSTR(%2$s,1,1) END)";
437724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4378de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
4379ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4380ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4381ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
4382ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
4383ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
4384ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
4385ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
4386ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
4387ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
4388ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4389ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
4390ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
4391ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
4392ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
4393ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
439424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
439524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // If the sort order contains one of the "is_profile" columns, we need to strip it out
439624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // first.
439724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (sortOrder.contains(Contacts.IS_USER_PROFILE)
439824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    || sortOrder.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
439924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                String[] splitOrderClauses = sortOrder.split(",");
440024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder rejoinedClause = new StringBuilder();
440124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                for (String orderClause : splitOrderClauses) {
440224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!orderClause.contains(Contacts.IS_USER_PROFILE)
440324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            && !orderClause.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
440424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (rejoinedClause.length() > 0) {
440524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            rejoinedClause.append(", ");
440624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
440724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        rejoinedClause.append(orderClause.trim());
440824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
440924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
441024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = rejoinedClause.toString();
441124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
441224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4413ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
4414ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
4415ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
4416ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
4417ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
4418ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
4419ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4420ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
4421ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
4422ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4423ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4424bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
4425ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
442624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
442724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user profile column varies depending on the view.
442824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String profileColumn = qb.getTables().contains(mDbHelper.getContactView())
442924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? Contacts.IS_USER_PROFILE
443024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : RawContacts.RAW_CONTACT_IS_USER_PROFILE;
443124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String sectionHeading = String.format(
443224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                AddressBookIndexQuery.SECTION_HEADING_TEMPLATE, profileColumn, sortKey);
4433bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
443424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
4435bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4436bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
4437bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
4438bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
4439bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
4440bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
4441bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
4442bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
4443ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
444424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
4445bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
4446ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
4447ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
4448ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
4449ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4450f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
4451ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
4452ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
4453ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4454ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
4455f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
4456ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
4457ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
4458bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
4459bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
4460bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4461bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
4462bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
4463bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
4464ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
4465f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
4466bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
4467bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
4468bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
4469bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
4470bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
4471bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
4472bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
4473bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
4474bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
4475bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
4476bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4477bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
4478bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
4479bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
4480bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
4481bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4482bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
4483bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
4484bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
4485ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4486ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4487e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return new AddressBookCursor((CrossProcessCursor) cursor, titles, counts);
4488ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
4489f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
4490ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4491ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4492ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
44932d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
449492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
449592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
449692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
449792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
44982d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
44992d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
45005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
45015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
45025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
450392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
450492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
450592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
450692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
450792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
450892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
450992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
451092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
451192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
451292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
451392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
451492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
451592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
451692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
451792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
451892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
451992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
452092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
452192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
45225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
45235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
45245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
45265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
45275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
45295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
45305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
45325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
45335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
45345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
45355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
45365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
45375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
45395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
45405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
45415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
45425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
45435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
45455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
45465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
45475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
45485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
45495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
455092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
45515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
45525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
45535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
45545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
45555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
45565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
45575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
45595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
45605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
45615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
45625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
45635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
45645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
45655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
45665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
45675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
45685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
456992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
457092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
45715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
45725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
45735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
45745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
45755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
45765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
45775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
45785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
45795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
45805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
45825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
45835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
458492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
458592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
45865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
45885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
45895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
45905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
459192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
45925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
45935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
45955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
45965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
459792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
45985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
45995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
460092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
460192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
460292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
460392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
46045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
46055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
460692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
460792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
460892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
46095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
46105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
461192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
461292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
46135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
461492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
461592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
461692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
461792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
461892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
461992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
462092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
462192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
462292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
462392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
462492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
462592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
462692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
462792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
462892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
462992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
463092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
463192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
463292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
463392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
463492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
46355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
46365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
463792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
463892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
463992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
464092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
464192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
464292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
464392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
464492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
464592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
464692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
464792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
464892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
464992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
465092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
465192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
465292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
465392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
465492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
465592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
465692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
465792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
46585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
46595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
46605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
46615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
466292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
466392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
46645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
46655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
46665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
46675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
46685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
46695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
46705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
46715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
46725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
46735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
46745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
46755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
46765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
46775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
46785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
46795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
46805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
46815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
46825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
468392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
468492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
468592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
46865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
46875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
46885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
46895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
46905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
46915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
46925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
46935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
46945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
46955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
46965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
46975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
46985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
469992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
470092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
470192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
470292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
470392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
470492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
470592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
470692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
470792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
470892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
470992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
4710ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
4711ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
4712ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
4713ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
47145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
47155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
47165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
47175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
47185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
47195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
47215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
47225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
47245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
47255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
47275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
47285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
47295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
47305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
47315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
47325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
47335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
47345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
47355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
47365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
47375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
47385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
47395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
47405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
47415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
47425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
47435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
47445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
47455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
47465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
47475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
47485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4749763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
4750763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
475182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
47527ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        sb.append(mDbHelper.getContactView(shouldExcludeRestrictedData(uri)));
47537ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
47547ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
4755916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
4756916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
4757916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
4758916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4759916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
4760916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
4761916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
4762916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
4763916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
47647ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            String[] projection, String filter, long directoryId) {
47657ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
47667ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
47677ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        sb.append(mDbHelper.getContactView(shouldExcludeRestrictedData(uri)));
4768916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
476903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
477003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
477103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
477203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
477330cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
477430cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
47755e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
47765e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            appendSearchIndexJoin(sb, uri, projection, filter);
47775e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
47787ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
47797ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
478003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
478103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
478203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
4783916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
478403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
478503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            StringBuilder sb, Uri uri, String[] projection, String filter) {
4786916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4787174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET)) {
478803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
478903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
479003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
479103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
479203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
479303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
479403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
47955e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
47965e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
47975e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
47985e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
47995e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
48005e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
48015e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
48025e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
48035e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
4804174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
4805174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens);
4806174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
4807174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(sb, filter, false, null, null, null, 0);
4808174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
4809174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
4810174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
4811174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
4812174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
4813174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            int maxTokens) {
4814174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
4815174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
4816174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
4817174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
4818174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
4819174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
48203716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
48213716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
48223716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        boolean singleTokenSearch = filter.split(QUERY_TOKENIZER_REGEX).length == 1;
48233716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4824174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
4825174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            emailAddress = mDbHelper.extractAddressFromEmailAddress(filter);
4826174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
4827174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
4828174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
482904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
483004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
483104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
483204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        mDbHelper.getCountryIso());
483304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
4834174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
4835174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
4836174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
4837174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
48385e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
48395e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
48403d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
48415e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
484204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
484304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
484404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
484504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
484604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
484704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
48483d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
48493d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
48503716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
48513716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
48523716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
48533716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
48543716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
48553716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
48563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
48573d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
48583d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
48593d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
48603d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
486104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
486204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
486304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
486404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
486504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
486604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
486704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
486804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
486904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
487004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
487104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
487204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
487304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
487404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
487504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
487604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
48775e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
48785e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
48793716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
48803716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
48813716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
48823716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
48833716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
48843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
48853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
48865e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
488703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
488804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
488904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
48903716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    // Optimization for single-token search.
48913716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    if (singleTokenSearch) {
48923716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
48933716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
48943716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
48953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
48963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
48973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
48983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
48993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
49003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
49013716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
49023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
49033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
49043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
49053716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
49063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
49073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
490804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
490904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
491004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
491103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
49125e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
49135e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
491403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
49155e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
49165e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
49175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(Tables.SEARCH_INDEX + " MATCH ");
49185e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
49192352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, "\"" + sanitizeMatch(filter) + "*\"");
49203d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
49212352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb,
492204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    "\"" + sanitizeMatch(filter) + "*\" OR \"" + phoneNumber + "*\""
49232352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                            + (numberE164 != null ? " OR \"" + numberE164 + "\"" : ""));
492403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
49252352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filter) + "*");
49269c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
492703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
4928a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
4929a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
49302352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    private String sanitizeMatch(String filter) {
49312352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        // TODO more robust preprocessing of match expressions
49322352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        return filter.replace('-', ' ').replace('\"', ' ');
49332352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
49342352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
49355e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
49365e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
49375e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
49385e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
49395e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
49405e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
49415e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
49425e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
49435e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
49445e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
49455e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
49465e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
49475e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
49485e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
49495e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
4950763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
4951763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
49527ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        sb.append(mDbHelper.getRawContactView(shouldExcludeRestrictedData(uri)));
4953763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
4954763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
4955763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
4956763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
4957763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4958a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
4959a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(mDbHelper.getRawEntitiesView(shouldExcludeRestrictedData(uri)));
4960a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
496146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
496246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
496346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
496482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
496582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
496646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
496746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
496846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
496946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
497046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
497146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
497246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
497346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
497446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
497582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4976a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getDataView(shouldExcludeRestrictedData(uri)));
497782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
497882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
4979a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
4980a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
4981a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
4982a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
49833296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
498446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
498546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
498646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
498746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
498882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
4989f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
4990f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
4991f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
4992f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
4993f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
499482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
4995ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
4996ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
49970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
49980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
49990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5000b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
50010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
5002a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5003a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
50040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5005a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5006a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
5007a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5008a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5009a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
5010a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
5011a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5012a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getEntitiesView(shouldExcludeRestrictedData(uri)));
5013a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
5014a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5015a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
5016a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5017a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
5018a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
5019a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5020a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5021a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
5022a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
5023a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5024a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5025a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
5026a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
5027a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5028a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
5029a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
5030a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
5031a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
5032a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
5033a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
5034a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
5035a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
5036a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
50370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5038a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
50390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5040a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
5041a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5042b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
50430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
50440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
50450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
50460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
50470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
50480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
5049a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
5050a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
50510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5052a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5053a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
505446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
505546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
505646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
505746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
505846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
505946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
5060a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
5061a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
5062a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5063a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
5064a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
5065a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
5066a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
5067a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5068a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5069a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5070a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
5071a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5072a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
5073a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
5074a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
5075a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5076a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5077a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
507824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
5079385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
5080385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
508124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
5082385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
5083385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
508424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
508524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
508624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
508724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
508824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
508924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void appendProfileRestriction(SQLiteQueryBuilder qb, Uri uri, String profileColumn,
509024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            boolean andRequired) {
509124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (!shouldIncludeProfile(uri)) {
509224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            qb.appendWhere((andRequired ? " AND (" : "")
509324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + profileColumn + " IS NULL OR "
509424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + profileColumn + "=0"
509524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + (andRequired ? ")" : ""));
5096385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
5097385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
5098385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
509924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private String prependProfileSortIfNeeded(Uri uri, String sortOrder) {
510024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (shouldIncludeProfile(uri)) {
510124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (TextUtils.isEmpty(sortOrder)) {
510224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC";
510324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            } else {
510424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC, " + sortOrder;
510524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
510624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
510724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return sortOrder;
510824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
510924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
511024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean shouldIncludeProfile(Uri uri) {
511124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user's profile may be returned alongside other contacts if it was requested and
511224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // the calling application has permission to read profile data.
511324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean profileRequested = readBooleanQueryParameter(uri, ContactsContract.INCLUDE_PROFILE,
511424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                false);
511524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (profileRequested) {
511624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(false);
511724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
511824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return profileRequested;
511924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
512024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5121a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private boolean shouldExcludeRestrictedData(Uri uri) {
5122a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        // Note: currently, "export only" equals to "restricted", but may not in the future.
5123a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
5124a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.FOR_EXPORT_ONLY, false);
5125a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (excludeRestrictedData) {
5126a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return true;
5127a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5128a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5129a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5130a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5131a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (requestingPackage != null) {
5132a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5133a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5134a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5135a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return false;
51360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
51370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
51384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
5139f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5140f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5141e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5142e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5143e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5144e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5145fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5146fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5147e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5148e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5149e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5150e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5151e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5152e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
51534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
51544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
51554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
51564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
51574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
51584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
51594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
51604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
51614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5162e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
5163f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5164f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5165e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5166e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5167e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5168e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5169fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5170fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5171e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5172e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5173e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5174e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5175e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5176e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
5177e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
5178e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
5179e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
5180e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
5181e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
5182e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
5183e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
5184e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
5185e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
5186e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
5187e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
5188e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
5189e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
5190e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
5191e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
51927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5193c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
5194c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
5195c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
5196c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
5197c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
5198f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
51992e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
5200c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
5201c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5202c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5203c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
5204c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
5205c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
5206c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
5207c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
5208c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
5209c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5210c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
5211c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
5212c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
5213c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5214c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5215c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5216c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
521700ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
5218d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
521970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
522070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
5221fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
522270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
522370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
522470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
522570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
5226d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
522770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
522867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
52295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
52305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
5231619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
5232619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
5233619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5234b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
5235f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
5236415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
5237415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
5238415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
5239b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
5240b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
5241a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
524224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
524324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, false);
5244f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
524524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
524624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
524724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(rawContactId)});
5248e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
5249b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5250e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
525124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
525224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForData(dataId, false);
5253f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
5254e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
525524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
5256d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5257d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5258f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
525949d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
526042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
526124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
526224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForContact(contactId, false);
526324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mSelectionArgs1[0] = String.valueOf(contactId);
526442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + "=?";
526542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
526642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
526742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
526842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
526942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
527042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                outputRawContactsAsVCard(localStream, selection, mSelectionArgs1);
5271f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
527242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
527342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
527442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
527549d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
527642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
527742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
527842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
527942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
5280d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
5281d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
528242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
528342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
5284d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
528542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
5286d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
528742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
528824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    long contactId = lookupContactIdByLookupKey(db, lookupKey);
528924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermissionForContact(contactId, false);
529024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
529142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
529242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
529342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
529442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
5295d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5296d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
5297d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
5298d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
5299d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5300d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                outputRawContactsAsVCard(localStream, selection, null);
5301f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
5302d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5303b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5304b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
5305fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
5306fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
5307b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
5308b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
5309b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5310f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,
5311e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
5312e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
5313e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
5314e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
5315e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
5316e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
5317e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5318e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
5319e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
5320e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
5321e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
532208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
5323f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
5324f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
532508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
532608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
532708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
532808ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
5329e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
5330e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5331d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
5332d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5333d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5334f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
5335d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
5336d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5337f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
5338d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
5339d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
5340d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5341d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
5342d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5343f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
5344f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
5345f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
5346d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
5347ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
5348ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
5349d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5350d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5351d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5352f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
5353f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
5354f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
5355f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
5356f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
5357f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
5358f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
5359f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
5360d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5361d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
5362d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
5363d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
5364d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5365d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
5366d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
5367d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
53687a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
53697a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa                new VCardComposer(context, VCardConfig.VCARD_TYPE_DEFAULT, false);
5370108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
5371108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
5372108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
5373108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            // No extra checks since composer always uses restricted views
5374108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (!composer.init(selection, selectionArgs)) {
5375108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
5376108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
5377108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
5378d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5379108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
5380108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
5381108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
5382108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
5383108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
5384108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
5385108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
5386108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
5387108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
5388108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
5389108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
5390108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
5391108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
5392d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5393d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5394d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5395b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
53964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
53974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
5398415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
5399415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
5400415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
5401a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
54024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
5403b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
5404be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
54052d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
5406b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
5407b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
540824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
5409b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
5410f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
541142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
541224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
5413f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
5414f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
5415f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                return "image/png";
5416b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
541724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
5418be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
5419b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
542024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
5421b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
5422f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
542324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
5424f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
5425508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
5426b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
542748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
542848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
542948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
543048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
54319005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
54329005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
543348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
543448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
543548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
543648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
543748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
543848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
543948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
544048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
5441b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
5442b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
5443b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
5444b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
5445b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
5446b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
5447b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
5448b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
5449c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
5450c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
5451c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
5452c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
5453d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
5454d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
5455d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
5456d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
545761efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
545861efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
54594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
54604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
54617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
546209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
546309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
546409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
546509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
546609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
546709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
546809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
546909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
547024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
547109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
547209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
54738727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
547424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
54758727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
54768727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
547709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
547809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
547924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
548009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
548109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
548209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
548309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
548424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
548524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
548609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
548709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
548809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
548909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
549009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
549109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
549209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
549309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
549409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
549524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
549609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
549709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
549809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
549909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
550009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
550109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
550209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
550309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
550409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
550509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
550609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
550709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
550809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
550909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
551009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
551109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
551209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
551309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
551409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
551509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
551609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
5517f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
5518f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5519f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
5520f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
5521f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5522f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5523f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5524f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
5525f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
552678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.insertNameLookup(rawContactId, dataId, lookupType, name);
5527f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5528f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5529f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5530f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
5531d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
5532f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5533f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5534f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
55352d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
5536d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5537d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
5538d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
5539d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
5540d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
5541d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
5542d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
5543e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
5544916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
5545916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
5546e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
5547e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
55489a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
55499a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
55509a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
55519a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
55529a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
55539a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
55549a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
55559a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
55569a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
55579a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
55589a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
55599a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
55609a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
55619a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
55629a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
55634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
55647a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
55657a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
55667a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
55677a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
55687a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
55697a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
55707a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
55717a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
55727a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
55737a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
5574f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
5575f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
55767a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
55777a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
55787a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
55797a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
55807a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
55817a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
55827a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
55837a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
55847a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
55857a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
55867a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
55877a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
55887a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
55897a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
55907a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
55917a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
55927a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
55937a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
55947a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
55957a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
55967a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
55977a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
55987a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
55997a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
56007a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
56017a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
56027a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
56037a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
56047a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
56057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
56067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
56077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
56087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
56097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
56107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
56117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
56124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
56134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
56144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5615b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
5616b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
5617b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
5618b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
5619b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
56204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
56214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5622b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
5623b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5624b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5625caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
56265e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
56275e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
56285e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
56295e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
56305e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
56315e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
56325e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
56335e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
56345e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
56355e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
56365e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5637caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
5638caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
5639caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
56405f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
5641caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
5642caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
5643caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
5644caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
56456f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
5646caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
56476f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
5648caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
5649f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
565073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
565173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
565273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
565373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
5654bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
5655bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
5656bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
5657bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
565873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
565973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
566073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
566173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
566273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
5663627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
5664627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
5665627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
5666627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
566773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
566873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
566973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
5670627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
5671627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
5672627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
5673627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
5674627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
567573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
567673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
567773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
567873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
567973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
568073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
568173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
5682627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
5683b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
5684d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5685f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
5686f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
5687f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5688f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
5689f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5690f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5691f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5692f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5693f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5694f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
5695f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
5696f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5697f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5698f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5699f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
5700f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5701f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
5702f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
5703f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5704f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5705f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
5706f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
5707f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
5708f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
5709f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
5710f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5711f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5712f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
5713f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
5714f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
5715f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
5716f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5717f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5718f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
5719f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5720f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5721f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
5722f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
5723f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5724f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
5725f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
5726f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
5727f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
5728f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
5729f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
57305fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
57315fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
57325fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
57335fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
57345fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
57355fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
57365fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
57375fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
57385fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
57395fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
57405fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
5741f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5742f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5743f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
5744f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5745f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
5746f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5747f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5748f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5749f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
5750f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
5751f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
5752f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5753f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5754f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5755f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
5756f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
5757f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
5758f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
5759f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
5760f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5761f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5762f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
5763f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
57645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
57650dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
57660dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
57670dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
57680dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
57690dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
57700dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
57710dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
57720dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
57730dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
5774bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
57750dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
57760dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
57770dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
57780dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
57790dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
57800dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
57810dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
578249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = mDbHelper.getWritableDatabase();
57830dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
57840dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
57850dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
57860dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
57870dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
57880dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
57890dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
57900dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE,
57910dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
57920dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
57930dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
57940dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
57950dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
57960dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
57970dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
57980dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
57990dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
58000dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
58010dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
5802bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
5803bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
58040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
58050dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
58060dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
58070dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
58080dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
58090dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
58100dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
58110dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
58120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
58130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
58149a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
58159a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
58169a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
58179a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
58189a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
58199a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
58209a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
58219a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
58229a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
582346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
582446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
582546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
582646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
582746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
582846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
582946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
583046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
583146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
583246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
583346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
583446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
583546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
583646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
583746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
583846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
583946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
584046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
584146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
584246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
584346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
584446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
584546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final Cursor cursor = mDb.query(
584646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDbHelper.getDataView(shouldExcludeRestrictedData(uri)),
584746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
584846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
584946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
585046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
585146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
585246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
585346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
585446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.update(Tables.CONTACTS, values2, Contacts._ID + "=?", mSelectionArgs1);
585546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
585646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
585746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
585846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
585946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
586046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
586146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
586246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
586346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
586446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
586546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
586646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
586746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
586846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
586946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
587046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private int updateDataUsageStat(
587146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            ArrayList<Long> dataIds, String type, long currentTimeMillis) {
587246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
587346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
587446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
587546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
587646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
587746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
587846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
587946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
588046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            mDb.beginTransaction();
588146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
588246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                final Cursor cursor = mDb.query(Tables.DATA_USAGE_STAT, columns, where, args,
588346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        null, null, null);
588446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
588546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
588646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
588746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
588846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
588946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
589046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
589146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
589246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
589346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            mDb.update(Tables.DATA_USAGE_STAT, values,
589446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
589546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
589646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
589746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
589846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
589946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
590046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
590146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
590246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
590346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        mDb.insert(Tables.DATA_USAGE_STAT, null, values);
590446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
590546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    mDb.setTransactionSuccessful();
590646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
590746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
590846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
590946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
591046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.endTransaction();
591146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
591246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
591346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
591446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
591546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
591646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
591746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
591846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
591946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
592046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
592146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
592246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
592346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
592446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
592546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
592646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
592746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
592846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
592946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
593046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
593146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
593246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
593346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
593446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
593546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
593646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
593746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
593846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
593946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
594046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
594146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
594246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
594346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
594446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
59454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
5946