ContactsProvider2.java revision fdd04bc3a972cd72dfe7bf925e1624d656b34cf7
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;
3071340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
3297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
3697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
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;
10971340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport android.provider.ContactsContract.DataUsageFeedback;
110d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
1113de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
112bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1133de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
11409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1153de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
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.
4212262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * second in_visible_group, then the rest.
4222262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
4232262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly
424c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
42546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
42646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
427c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
428c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
4292262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
4302262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
43146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + "(CASE WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_CURRENT
43246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
43346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + " WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_RECENT
43446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
43546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
43646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
43746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
43846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
439c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
440c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
44146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
44246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
44346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
444c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
445916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
446916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
447916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
448916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
44992ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
450916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
451f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
452f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
453f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
454f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
455f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
456f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
457f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
458f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
459f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
460f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
461f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
462f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
463f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
464f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
465f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
466916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
467f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
468f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
469f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
470f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
471f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
472f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
473f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
474f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
475f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
476f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
477f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
4783d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
4793d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
480f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
481f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
482f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
483f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
484f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
485cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
486f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
487f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
488f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
489f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
490f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
491f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
492f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
493f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
494f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
495f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
496f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
497f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
498f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
499f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
500f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
501f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
502f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
503f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
504f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
505f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
50603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
507f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
508f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
509f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
510f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
511f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
512f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
513f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
514f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
515f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
516f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
517f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
518f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
519f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
520f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
521f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
522f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
523f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
524f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
525f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
532f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
533f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
580038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
585e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
59024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
595916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
600916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6015e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, Contacts.TIMES_CONTACTED)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
612f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
614fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
619ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
63724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
642a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.IS_RESTRICTED)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
65024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
656a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.IS_RESTRICTED)
66524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
68024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
69224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
698f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
701f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
704f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
705f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
7093d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
7103d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
712f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
716f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
7172530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
720ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
738c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
745ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ")")
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Contacts.HAS_PHONE_NUMBER + ")")
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
762373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
770eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
80882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
817f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
821f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
823f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
824f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
825f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
826f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
827f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
828f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
829f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
8301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
831f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
832f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
833f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
834f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
835f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
838f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
839d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
840f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
841f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
844f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
845f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
846f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
847f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
848f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
849778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
850778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
851f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
8527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
8539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
8549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
8559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
8569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
8579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
8589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
8592526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
8602526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
861bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
862bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
863bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
864bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
86551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
86603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
86703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
86803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
86903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
87003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
8719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
8729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
8739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
874f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
8751129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
8761129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
8772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
8782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
879f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
880f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
88146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
88246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
88346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
88446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
88546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
88646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
8874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
8884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
889a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
890d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
891d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
892a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
893a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
8943653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
8953653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
8962d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
8972d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
898a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
899c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
9005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
9015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
9022149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
9035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
9042149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
9052149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
906a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
907a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
908a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
909a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
910f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
91142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
91242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
9135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
914ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
915ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
9165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
9173653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
9185ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
9195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
9205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
92146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
92246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
92346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
924b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
9254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
9264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
927ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
92848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
9295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
930ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
9314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
93248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
9331dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
9345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
9355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
9364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
937ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
93848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
93946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
94046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
9411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
942ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
943ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
944ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
945ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
94635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
947b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
948b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
94935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
950a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
951b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
952b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
953b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
954b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
9554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
956eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
957eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
95882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
95982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
9601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
961c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
962c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
963c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
964c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
9652d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
966c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
967c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
9681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
9691b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
9701b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
9711b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
9721b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
9731b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
9741b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
9751b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
97609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
97709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
978d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
979d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
980d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
9817a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
9827a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
98324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
98424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
98524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
98624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
98724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
98824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
98924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
99024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
99124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
99224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
99324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
99424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
99524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
99646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
99746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
99846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
99946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
100046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
100146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
100246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
100346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
100419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
100519a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1006d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1007d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1008d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1009d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1010d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1011d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1012d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1013d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1014d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
10154458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
10164458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1017d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
10183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
1019ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1020ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1021ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1022e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1023ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1024ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1025ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1026ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1027ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1028a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1029e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1030e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1031e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1032e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1033e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
103424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
103524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Cached information about the contact ID and raw contact IDs that make up the user's
103624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile entry.
103724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
103824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static class ProfileIdCache {
103924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean inited;
104024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        long profileContactId;
104124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileRawContactIds = Sets.newHashSet();
104224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileDataIds = Sets.newHashSet();
104324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
104424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        /**
104524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * Initializes the cache of profile contact and raw contact IDs.  Does nothing if
104624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * the cache is already initialized (unless forceRefresh is set to true).
104724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param db The contacts database.
104824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param forceRefresh Whether to force re-initialization of the cache.
104924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         */
105024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        private void init(SQLiteDatabase db, boolean forceRefresh) {
105124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (!inited || forceRefresh) {
105224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileContactId = 0;
105324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileRawContactIds.clear();
105424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileDataIds.clear();
105524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                Cursor c = db.rawQuery("SELECT " +
105624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_CONTACT_ID + "," +
105724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "," +
105824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        DataColumns.CONCRETE_ID +
105924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " FROM " + Tables.RAW_CONTACTS + " JOIN " + Tables.ACCOUNTS + " ON " +
106024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" +
106124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        AccountsColumns.PROFILE_RAW_CONTACT_ID +
106224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " JOIN " + Tables.DATA + " ON " +
106324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_RAW_CONTACT_ID,
106424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        null);
106524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                try {
106624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    while (c.moveToNext()) {
106724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (profileContactId == 0) {
106824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            profileContactId = c.getLong(0);
106924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
107024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactIds.add(c.getLong(1));
107124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileDataIds.add(c.getLong(2));
107224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
107324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                } finally {
107424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    c.close();
107524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
107624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
107724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
107824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
107924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
108024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private ProfileIdCache mProfileIdCache;
108124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
10823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1083b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
108431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
10854097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1086f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1087315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1088622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1089622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
109072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
1091622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1092f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1093a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1094d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1095f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1096a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
109720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
109873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
109920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
110009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
11013826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
110209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
110315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
110415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
110515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1106bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
110773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1108d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private TransactionContext mTransactionContext = new TransactionContext();
1109de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
11101a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
11111a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
111281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
111381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
11144cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
11153826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1116d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1117bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1118bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1119bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
11204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
11214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1122de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1123ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1124ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1125ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1126ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1127ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1128ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1129ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
113035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1131ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
113215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
113315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
113415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
113524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache = new ProfileIdCache();
1136b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
113772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1138a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
113965ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1140bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
114115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
114215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
114372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1144bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1145bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1146bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1147bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1148bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1149bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1150bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1151bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1152bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
11532a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
115415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1155bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1156bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1157bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1158bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
115905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1160bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
116115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
11623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
116349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
11644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
11654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1166767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
116751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
116851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
116904b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
117015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
117115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
11724cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
117304b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
11744cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
11754cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
117651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
1177cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
11785b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
117915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
11805b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1181f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
11825b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1183bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
1184bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1185bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE,
11866d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForEmail(context, mDbHelper, mContactAggregator));
1187bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
11886d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForIm(context, mDbHelper, mContactAggregator));
1189bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE,
11906d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForOrganization(context, mDbHelper, mContactAggregator));
1191bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE,
11926d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoneNumber(context, mDbHelper, mContactAggregator));
1193bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
11946d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNickname(context, mDbHelper, mContactAggregator));
1195bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
11966d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredName(context, mDbHelper, mContactAggregator,
1197bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
1198bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
11996d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredPostal(context, mDbHelper, mContactAggregator,
1200bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
1201bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE,
12026d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForGroupMembership(context, mDbHelper, mContactAggregator,
1203bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
1204bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE,
12056d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoto(context, mDbHelper, mContactAggregator));
12066d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov        mDataRowHandlers.put(Note.CONTENT_ITEM_TYPE,
12076d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNote(context, mDbHelper, mContactAggregator));
1208bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1209bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1210bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1211bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1212bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1213bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1214bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1215bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1216bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1217bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1218bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1219bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1220bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1221bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1222bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1223bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1224bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1225bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1226bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
122715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
122815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
122915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
123015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
123115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
123215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
123315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
123415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1235bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
123615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
123715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1238bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1239bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1240bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1241bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1242bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1243bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1244bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1245bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1246bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1247bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1248bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1249bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
125015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
125115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
125215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
125315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
125415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
125515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
125615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
1257bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
1258bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1259bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1260bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1261bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1262bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1263bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1264bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1265bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1266bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1267bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1268fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1269fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1270fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1271fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1272fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1273bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1274bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1275bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1276bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1277bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1278bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1279bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
128005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
128105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
128205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
128305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
128405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1285bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1286bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1287bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1288bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1289bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1290bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1291bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1292bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1293bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1294bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1295bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1296bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
12974cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
12984cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
129953fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
13003826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
13013826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
13024f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
13034f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
13044f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1305fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
13064cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
130751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
130851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
130951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
131051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
131151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
131251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
131351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
131451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1315bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1316f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1317f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1318f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1319f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1320f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1321f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
132251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
132351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
132451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
132551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
132651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
132751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
132851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
132951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
133051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
1331bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, currentLocale);
1332bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1333bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1334bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
133551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1336fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1337fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1338fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1339fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1340fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1341fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1342fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1343fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
1344fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1345fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
1346fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1347fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1348fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
1349fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1350fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
1351fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1352fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1353fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1354fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1355fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
135605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
135705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
135805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
135905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1360bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1361bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
136251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
136351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
13643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
13653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
13663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
13673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
13683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
13693826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
13703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mContactsAccountCount == 0
137149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                && DatabaseUtils.queryNumEntries(mDbHelper.getReadableDatabase(),
137249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                        Tables.CONTACTS, null) == 0) {
13733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
13743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
13753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
13763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
13773826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
13783826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
137931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1380de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1381b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1382b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
138331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
138431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1385013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1386013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1387013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1388013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
13895df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
13905df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
13915df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
13925df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
13935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1394ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
139572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
139672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
139772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
139872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
13995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
14005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
14015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
14025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
14033d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
1404b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1405b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
14063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
14073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1408568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1409568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1410568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1411568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1412568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1413bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1414568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1415bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1416bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1417bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1418568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1419bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1420bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, mCurrentLocale);
1421bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1422568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1423bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1424bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1425bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1426bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1427bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1428bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1429568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1430568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1431bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1432bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1433bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1434bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1435bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1436bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1437bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1438bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1439b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
1440b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1441b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1442bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1443bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1444bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1445bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1446bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1447bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1448bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1449bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1450bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1451bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1452bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1453bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1454bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1455bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1456bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1457bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1458bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1459bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1460bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1461bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1462bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1463bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1464bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1465bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1466bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1467bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1468bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1469bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1470bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
14713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
14723d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
14733d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1474568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
14750e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
14763d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
14773d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1478bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1479bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1480bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1481bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1482bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1483bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
14843d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
14853d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
14863d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1487bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1488bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
14893d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
14903d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1491a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1492a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1493a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1494a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1495b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
14963826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1497a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1498a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1499568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
150015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1501568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1502568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1503568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1504568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1505568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
150615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
150715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
150815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
150915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
151015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
151115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
151215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
151315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
151415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
151515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
151615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1517ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1518568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1519568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1520568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1521568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1522568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
152315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1524568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1525568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1526568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1527568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1528568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
152915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
1530bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
1531bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
1532bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
1533bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
1534bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
1535bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
1536bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
1537bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1538bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
1539bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
1540bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
1541bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
1542bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1543bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
154415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1545568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1546568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1547568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1548568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1549568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
155015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1551568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1552568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1553568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1554568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1555568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1556568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
155715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1558568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1559568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1560568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
15614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
15627b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
15637b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
15647b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        return super.bulkInsert(uri, values);
15657b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
15667b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
15677b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
1568285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1569bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1570b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1571b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1572285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
15731ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1574d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1575b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1576b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1577285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1578285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1579285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
15801129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1581bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1582b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1583b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1584285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1585b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
1586bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
15871a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
15881a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1589b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
15901a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
15913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
1592bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
1593bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
15943826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
15953826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
15963826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
15973826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
1598b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1599b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1600bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
1601bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleContacts = mTransactionContext.getStaleSearchIndexContactIds();
1602bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleRawContacts = mTransactionContext.getStaleSearchIndexRawContactIds();
1603bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
1604bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
1605bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mTransactionContext.clearSearchIndexUpdates();
1606bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
1607bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
1608bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
1609b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1610bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1611b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1612b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
16131129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
161424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Determine whether we need to refresh the profile ID cache.
161524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean profileCacheRefreshNeeded = false;
161624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1617d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (long rawContactId : mTransactionContext.getInsertedRawContactIds()) {
16188ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov            mDbHelper.updateRawContactDisplayName(mDb, rawContactId);
1619bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.onRawContactInsert(mTransactionContext, mDb, rawContactId);
1620285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1621b5a4add17815167d20a90645779df34cdf45280dFred Quintana
162224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Map<Long, Account> insertedProfileRawContactAccountMap =
162324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mTransactionContext.getInsertedProfileRawContactIds();
162424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (!insertedProfileRawContactAccountMap.isEmpty()) {
162524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            for (long profileRawContactId : insertedProfileRawContactAccountMap.keySet()) {
162624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mDbHelper.updateRawContactDisplayName(mDb, profileRawContactId);
162724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mContactAggregator.onProfileRawContactInsert(mTransactionContext, mDb,
162824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactId,
162924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        insertedProfileRawContactAccountMap.get(profileRawContactId));
163024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
163124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = true;
163224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
163324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1634d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> dirtyRawContacts = mTransactionContext.getDirtyRawContactIds();
1635d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
1636a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1637a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1638d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
1639a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1640a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
164124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
164224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
164324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, dirtyRawContacts);
1644a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1645a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1646d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> updatedRawContacts = mTransactionContext.getUpdatedRawContactIds();
1647d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
1648a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1649a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1650d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
1651a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1652a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
165324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
165424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
165524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, updatedRawContacts);
1656b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1657b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1658d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (Map.Entry<Long, Object> entry : mTransactionContext.getUpdatedSyncStates()) {
1659b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
16609d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
16619d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
16629d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
16639d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
1664b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1665b5a4add17815167d20a90645779df34cdf45280dFred Quintana
166624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (profileCacheRefreshNeeded) {
166724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Force the profile ID cache to refresh.
166824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mProfileIdCache.init(mDb, true);
166924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
167024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1671d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1672b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1673b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1674a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1675a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1676a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1677a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1678d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
1679b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1680a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1681b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1682a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1683a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1684285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1685285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
168624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
168724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given contact ID represents the user's personal profile - if it is, calls
168824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * a permission check (for writing the profile if forWrite is true, for reading the profile
168924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * otherwise).  If the contact ID is not the user's profile, no check is executed.
169024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param contactId The contact ID to be checked.
169124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
169224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
169324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermissionForContact(long contactId, boolean forWrite) {
169424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache.init(mDb, false);
169524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileContactId == contactId) {
169624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
169724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
169824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
169924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
170024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
170124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given raw contact ID is a member of the user's personal profile - if it
170224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * is, calls a permission check (for writing the profile if forWrite is true, for reading the
170324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the raw contact ID is not in the user's profile, no check is
170424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * executed.
170524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param rawContactId The raw contact ID to be checked.
170624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
170724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
170824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermissionForRawContact(long rawContactId, boolean forWrite) {
170924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache.init(mDb, false);
171024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileRawContactIds.contains(rawContactId)) {
171124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
171224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
171324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
171424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
171524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
171624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given data ID is a member of the user's personal profile - if it is,
171724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * calls a permission check (for writing the profile if forWrite is true, for reading the
171824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the data ID is not in the user's profile, no check is executed.
171924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param dataId The data ID to be checked.
172024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
172124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
172224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermissionForData(long dataId, boolean forWrite) {
172324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache.init(mDb, false);
172424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileDataIds.contains(dataId)) {
172524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
172624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
172724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
172824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
172924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
173024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Performs a permission check for WRITE_PROFILE or READ_PROFILE (depending on the parameter).
173124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * If the permission check fails, this will throw a SecurityException.
173224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
173324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
173424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermission(boolean forWrite) {
173524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String profilePermission = forWrite
173624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? "android.permission.WRITE_PROFILE"
173724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : "android.permission.READ_PROFILE";
173824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        getContext().enforceCallingOrSelfPermission(profilePermission, null);
173924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
174024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1741285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1742cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
174381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
174481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
174581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
174681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
174781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
174881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
174981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
1750cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
1751568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
175251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
17533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
17543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
17553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
17563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
175751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
175851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1759f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
17603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
17613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
17626d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
17636d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                    getContext(), mDbHelper, mContactAggregator, mimeType);
17643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
17653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
17663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
17673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
17683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
17694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1770de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
1771bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
17721129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
1773b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1774f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1775f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
1776f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
1777f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1778a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
1779a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
178035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1781a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
178235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
1783b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
178435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
178535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1786d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
1787d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
17886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
17896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
17906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
179124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
179224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
179324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
179424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
179524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
17965ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
179724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, false);
1798f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1799a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1800a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1801a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
18025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
18035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
1804f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1805f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1806a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1807a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1808a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
180924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
181024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(true);
181124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, true);
181224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
181324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
181424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
181524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1816a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
1817f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1818f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1819a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1820a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1821a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1822ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1823f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
1824f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1825ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1826ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1827ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1828eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
18295aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
183043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
1831eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
1832eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
1833eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
183482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
183582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
18361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
18371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
18381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1839a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
184081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
1841f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
1842a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1843a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
18447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
18457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
18467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
18477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1848de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
1849a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1850a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1851a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1852e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
1853e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
1854e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
1855e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
1856e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
1857e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
1858e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
1859e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
1860e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
1861e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
1862e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
1863e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
1864e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
18657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
1866e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
1867f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
1868f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
1869e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
1870f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1871f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1872f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
1873e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
1874e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
1875e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1876e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
1877e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
1878fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
1879fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
1880e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
1881e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1882e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
1883e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
1884e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
1885e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
1886e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1887e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
1888e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
1889e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
1890e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
1891e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
1892fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
1893fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
1894e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
1895e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
1896e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
1897f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
1898f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
1899e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
1900f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
1901f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
1902e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
1903e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
1904f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
1905f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1906e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
1907f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
1908f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
1909f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
1910f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
1911035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1912f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1913e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
19147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
19157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
1917d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
19186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
19196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
19206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
19216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
1922d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
1923de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
19246bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
19256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
19266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
192724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
1928a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1929f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
1930f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
1931dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
193224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forProfile Whether this raw contact is being inserted into the user's profile.
1933a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1934a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
193524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter,
193624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            boolean forProfile) {
1937f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
1938f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
1939f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
1940f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1941e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
19427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
19443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
1945f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
19463d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
19473d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1948f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
1949f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
195024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
195124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Profile raw contacts should never be aggregated by the aggregator; they are always
195224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // aggregated under a single profile contact.
195324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            aggregationMode = RawContacts.AGGREGATION_MODE_DISABLED;
195424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
1955f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
1956f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
1957f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
1958285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
195924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
196024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of the user profile Contact (or association with the existing one)
196124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // at the end of the transaction.
196224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mTransactionContext.profileRawContactInserted(rawContactId, account);
196324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else {
196424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of a Contact based on this RawContact at the end of transaction
196524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mTransactionContext.rawContactInserted(rawContactId, account);
196624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
1967f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1968dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
1969dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
1970dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
1971dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
1972dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
1973dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1974dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1975dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
19763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
1977023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
1978a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1979a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1980dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
1981dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
1982dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
1983dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
1984dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
1985dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1986dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
1987dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1988dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
1989dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
1990dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
1991dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
1992dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
1993dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
1994dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
1995dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
1996dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1997dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
1998dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
1999dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2000dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2001dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2002dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2003dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2004dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2005dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2006dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2007dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2008dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2009dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2010dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2011dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2012dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2013dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2014dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2015dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2016dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2017dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2018dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2019dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2020dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2021dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2022dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2023dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2024dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2025dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2026dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2027dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2028dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2029dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2030dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2031dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2032a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2033a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2034a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2035a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2036a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2037a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2038f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2039a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2040de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2041de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
204267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2043de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
204420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
204524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // If the data being inserted belongs to the user's profile entry, check for the
204624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // WRITE_PROFILE permission before proceeding.
204724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForRawContact(rawContactId, true);
204824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2049de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2050de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2051de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2052b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2053de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2054de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2055508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2056de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2057de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2058de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2059de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2060de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
20614097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2062b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2063de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2064a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2065a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2066d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        id = rowHandler.insert(mDb, mTransactionContext, rawContactId, mValues);
2067f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2068d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.markRawContactDirty(rawContactId);
2069a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2070d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactUpdated(rawContactId);
2071a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
20724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
20734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2074ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
20758ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov        mDbHelper.updateRawContactDisplayName(db, rawContactId);
2076d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2077d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
20789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
207920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
208020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2081f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
208220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
208320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2084de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2085de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
2086f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS,
2087f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2088de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2089de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
2090f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
209124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
209224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for write profile permission if the data belongs to the profile.
209324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, true);
209424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2095f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
2096a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2097d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                count += rowHandler.delete(mDb, mTransactionContext, c);
2098f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
2099d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                    mTransactionContext.markRawContactDirty(rawContactId);
210088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
210120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
210220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2103de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
210420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
210520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
210620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
210720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
210820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
210988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
211088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
211188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
211220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2113f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
211488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
211588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
21164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
2117f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
21184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2119f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
212020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
212120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
212220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
212320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
212420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2125f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
212620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
212720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
212820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
212920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
213020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
213120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
213220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
213320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
213420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
21357a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
213620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
213720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
213820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
213924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Check for write profile permission if the data belongs to the profile.
214024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
214124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermissionForRawContact(rawContactId, true);
214224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2143a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2144d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return rowHandler.delete(mDb, mTransactionContext, c);
214520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
214620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
214720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
214820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
214920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
215020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2151ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2152ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2153f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2154f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2155f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2156f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2157e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
2158ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2159ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2160f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
216167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2162f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
216367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2164f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2165ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2166dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
2167dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
2168dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
2169dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2170f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2171f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
217273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
217373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2174f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2175ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2176dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
2177dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
2178dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
2179dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
2180dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
2181dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
2182dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
2183dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
2184dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2185dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
2186dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
2187dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
2188dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2189dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
2190dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
2191dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
2192892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
2193892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
2194892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
2195892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
2196892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
2197d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mTransactionContext.markRawContactDirty(rawContactId);
2198892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
2199dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2200892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
2201892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
2202dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2203dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2204dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2205f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
22061a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2207ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2208ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2209ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2210ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2211ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
22125aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2213e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
22145aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
22151a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
22161a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2217e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
22181a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2219e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2220e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2221e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2222ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
222382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
22241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
222582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
222682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
22270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
22284dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
22294dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
22300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
223182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
22324dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
22334dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
22344dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
22354dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
22361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
22371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2238dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2239dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
224082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2241f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
22422526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
2243dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2244dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2245dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
22462526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
22472526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
22481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2249dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2250dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
22510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
22520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
22530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
22540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2255dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2256dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2257dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
22582a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mDbHelper.getMimeTypeIdForIm());
2259dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
22602a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mDbHelper.getMimeTypeIdForEmail());
2261f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2262f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2263f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2264f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2265f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2266f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2267f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
22682526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
22692526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
22702526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
22712526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
22722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
22732526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
22742526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
22752526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
2276dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
22772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
22782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2279dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
22802526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
22812526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
2282dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
22832526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
22842526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
22852526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
22862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
22872526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
22882526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
2289dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
22902526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
22912526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2292dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2293dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
22941f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
229582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
22962526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
22972526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
2298dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
229970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
2300f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
230170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
23021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
23031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2304de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
23052526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
23064394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
23071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
230867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
23095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2310e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
23111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
23121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
23131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
23141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
23151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
231631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
231731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
231831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
23191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
23201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
232182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2322a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2323a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2324a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2325a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2326a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2327a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2328a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
232982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2330a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2331a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
233282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
233382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
233482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
233582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
233682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2337a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
233882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
233982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
2340aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
2341aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
23421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2343a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2344a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2345a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2346e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
23470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
234882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
234982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
23500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
23510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
23520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
23530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
23540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
23550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
23560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
23570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
23580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
23590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
23600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
23610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2362a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
236378fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteStatusUpdate(dataId);
236482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
236582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
236678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.replaceStatusUpdate(dataId, timestamp, status, resPackage, iconResource,
236778fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
2368a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
236978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.insertStatusUpdate(dataId, status, resPackage, iconResource,
237078fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
2371e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2372e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2373bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2374a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2375f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
2376a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2377a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2378a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
23791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
23801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
23814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2382de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2383bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2384b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2385b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2386b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2387f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2388f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2389508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2390508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
239135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2392b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
239335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2394b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2395b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2396b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2397b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2398b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2399b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2400cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2401cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2402cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2403cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2404cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2405d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2406d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2407dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
24086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
24096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
24109fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
24112e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
24122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
24132e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
2414fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2415fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
24162e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
24172e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
24182e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
2419dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
24202e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
24212e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
24229fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
24239fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
24249fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
24259fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
24269fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
24279fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
2428a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
24299fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
24309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
24319fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
24329fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
24339fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
24349fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
24359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
24369fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
243760de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
24389fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
24399fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
24409fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
24419fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
24429fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
24439fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
2444dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
24459fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
24469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
24479fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
24489fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
24499fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
24509fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
24519fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
24529fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
24539fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
24549fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
24552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
24562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
2457fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
2458fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
2459e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
24602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
24612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
24622971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2463fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
2464fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
2465fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
24662971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
24672971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
24682971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
24692971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
24702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
24712971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
24722971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
24735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
24742971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2475fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
2476fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
2477508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2478508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
247920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2480f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2481944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2482f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
248320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
248420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
248548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
248648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
248748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
248848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2489508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2490f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
24914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
24924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
2493ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2494ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2495ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2496f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
24975aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
24982971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
24992971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
25002971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
25012971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
25022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2503e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
25042971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
25052971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
25065aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
25072971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
25082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
25092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
25102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
251181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2512f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
251381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
25142971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2515508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2516508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2517eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
251843880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2519e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
2520eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2521eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
252282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
25230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
25241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
25251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
252681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
252781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
25283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
252981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2530508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
25314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
25324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
25331c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
2534ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2535b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
253694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2537de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
253894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
253994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
254094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
254194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2542f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2543de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
254494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
254594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
254694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2547f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2548de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
254994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
255094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
25511a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
255294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
255394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
255494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
25555aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2556e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
25571a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
2558e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2559e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2560e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2561dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
256224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForContact(contactId, true);
256396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
2564cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
256596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
256696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
2567cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2568cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2569cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2570dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
2571cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2572cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2573cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2574cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2575cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
25763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
25773826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2578cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2579cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2580cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2581fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
258224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForRawContact(rawContactId, true);
25833389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
25843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
25853826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2586f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
258714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2588fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
2589fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
2590fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
259133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2592b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2593dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
259433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
259533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
259633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
25970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
25989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
25999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
26009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
26019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
26029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
26039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
26049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
26059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
26060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
26070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2608dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
260981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
261081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2611cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2612cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2613cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2614cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2615cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2616cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2617dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
2618cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2619cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
26204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2621de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2622de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2623bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2624b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2625b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2626b5a4add17815167d20a90645779df34cdf45280dFred Quintana
262735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
262800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
262900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2630b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2631b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
26321129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
2633d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.syncStateUpdated(rowId, data);
2634b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2635b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2636b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2637f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2638f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
263900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
264035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2641b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2642b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2643b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2644b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2645b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2646b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2647b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2648b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2649b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2650b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2651b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
265235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2653d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2654dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
265500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
265600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
265700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2658d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2659dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
2660c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2661c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2662c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
266324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
266424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Restrict update to the user's profile.
266524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder profileSelection = new StringBuilder();
266624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileSelection.append(Contacts.IS_USER_PROFILE + "=1");
266724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (!TextUtils.isEmpty(selection)) {
266824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    profileSelection.append(" AND (").append(selection).append(")");
266924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
267024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                count = updateContactOptions(values, profileSelection.toString(), selectionArgs,
267124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        callerIsSyncAdapter);
267224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
267324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
267424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
26752e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
26762e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
26772e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
26782e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
26792e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
2680fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2681fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
26822e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
26832e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
26842e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
2685dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
26862e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
26872e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
26882e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
26897d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
26907d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
26917d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
26927d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
26937d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
26947d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
26957d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
26967d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
26977d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
26987d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
269920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2700944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2701f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
270281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2703f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
270481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
270520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
270620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2707c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
270848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
270948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
271048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
271148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2712f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
271381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2714f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
271581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
271600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
271700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
27187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
27195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
27205ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
2721dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
27227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
27237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
27247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
27255ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
272633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
27274529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
27284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
27294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
2730dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
2731dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
27324529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
27334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
2734dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
2735dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
27364529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
27377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
27387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
27397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2740ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
27415aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
2742f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
274381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2744f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
274581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2746ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2747ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2748ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2749ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2750ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
27514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
27524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
275373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
27545aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
27555aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
275681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2757f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
275881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2759ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2760ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2761ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2762127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
2763de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
2764b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2765b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2766b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2767eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
2768e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
2769e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
277043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2771eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2772eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2773eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
27749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
27759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
27769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
27779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
27789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
277972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
2780bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
278172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
2782d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
2783d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
2784d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
278546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
278646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
278746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
278846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
278946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
279046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
279146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
279246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
279346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
279481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
279581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2796f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
279781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
279800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
279900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
280000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
28014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
28024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
28039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
28049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
28059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
28069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
28079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
28089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
28099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
28109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
28119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
28129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
28139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
28149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
28159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
28169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
28179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
28189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
28199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
28209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
28219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
28229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
28239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
28249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
28259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
28269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
28279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
28289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
28299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
28309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
28319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
28329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
28339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
28349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
28359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
28369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
28379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
28389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
28399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
28409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
28419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
28429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
28439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
28449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
28459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
28469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
28479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
28489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
28499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
28509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
28519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
28529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
28539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
28549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
28559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
28569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
2857aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
2858aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
28599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
28609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
28619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
28625aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
2863f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
286473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2865ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2866ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
286773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
2868f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
286973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
287073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
287173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
287273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
287373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
287473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
287573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
287673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2877ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
28781a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
28791a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
288094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
28816ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
28821129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
28836ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
2884e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
28856ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
28866ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
28876ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
28886ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
28896ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
28906ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
28916ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
289224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
28936ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
2894ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
28956ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
28966ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
28976ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
28986ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
28996ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
29006ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
29016ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
29026ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
290394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
290494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
290594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
2906b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
2907b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
2908e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
29091a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
29101a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2911e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2912e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2913e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2914e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2915dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
2916dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
29174529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
29184529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
29194529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
29204529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
292173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
292297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
292397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
292497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
292597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
292697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
29274529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
2928b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
292951bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
29304529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
29314529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
29324529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
29334529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
2934dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
29354529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
29364529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
29374529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
29384529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
29394529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
29404529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
29414529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
29424529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
29434529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
2944dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
2945dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
294624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
294724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Enforce profile permissions if the raw contact is in the user's profile.
294824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForRawContact(rawContactId, true);
294924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
295096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
295196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
295219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
295319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
295419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
2955ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
2956ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
295719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
295819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
295996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
296019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
296119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
296219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
2963ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
2964ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
296519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
296619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
296719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
296819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
296919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
297019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
297119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
2972f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
297396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
29745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
2975f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
2976f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
2977f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
2978f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
2979f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
2980f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
298169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
2982f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
2983f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
2984433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
2985dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
2986dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
2987dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
2988dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
29894529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
2990dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2991dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
2992dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
2993dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
2994dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
2995dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
2996dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
2997dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
2998dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
2999dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
3000dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3001dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3002dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3003dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
3004dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3005dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3006dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3007433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3008dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3009285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
30102b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3011285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3012f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3013f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3014f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3015f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3016f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
301778fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.resetNameVerifiedForOtherRawContacts(rawContactId);
3018f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3019f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3020f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
302119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
3022d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mTransactionContext.rawContactInserted(rawContactId,
3023d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        new Account(accountName, accountType));
302419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
30255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
30265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
302733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
302833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3029321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3030f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
303120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
303220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
303320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
30345ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
303520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
303620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
303720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
303820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
303920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3040b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
304120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
304220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
304397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
304497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
304597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
304697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
304797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
3048653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
304920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3050653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3051653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
3052f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(uri, DataRowHandler.DataUpdateQuery.COLUMNS,
3053f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
3054653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3055653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
305624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check profile permission for the raw contact that owns each data record.
305724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = c.getLong(DataRowHandler.DataUpdateQuery.RAW_CONTACT_ID);
305824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, true);
305924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3060f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
306120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3062653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3063653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
306420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
306520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3066653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
306720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
306820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3069f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3070653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3071653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3072321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3073653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
3074f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
3075a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3076d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (rowHandler.update(mDb, mTransactionContext, values, c, callerIsSyncAdapter)) {
3077813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
3078813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
3079813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
3080a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
3081321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3082321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
30838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
3084dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
30858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3086b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
308724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                new String[] { Contacts._ID, Contacts.IS_USER_PROFILE }, selection,
30888c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
30898c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
30908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
30918c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
309224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
309324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for profile write permission before updating a user's profile contact.
309424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean isProfile = cursor.getInt(1) == 1;
309524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (isProfile) {
309624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermission(true);
309724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
309824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3099dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
31008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
31018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
31028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
31038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
31048c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
31058c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31068c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
31078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
31088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3109dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
3110dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
3111d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
311224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Check write permission if the contact is the user's profile.
311324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForContact(contactId, true);
311424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
31158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3116b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3117d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3118b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3119d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3120b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3121d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3122b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3123d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3124b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3125d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3126d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3127d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
31288c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3129d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3130d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3131d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
31328c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3133c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
31348c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3135c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3136c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
31374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
313897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
313997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
31408c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3141dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
3142dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
3143dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
3144dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
3145dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
3146dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
3147dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
3148dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3149dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
3150dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3151dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
3152dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
3153dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3154dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3155dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
31568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
31578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
31588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3159b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
31608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3161b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
31628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3163b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
31648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3165b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
31668c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3167b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
31688c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
31698c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31709b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
31716e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
31729b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
31739b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
31749b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
31759b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
31769b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
31779b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
3178f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3179d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3180127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3181127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
31820c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
31830c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
318480c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
3185ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
3186ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
31870c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
31880c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
31890c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
31900c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
31910c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
31920c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3193b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3194127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
31950c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
31964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
31974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
31980c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
31994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
32004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
32010c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
32026bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
32036bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
32040c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
32050c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
32060c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
32070c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3208127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3209127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
32103389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
321169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
321269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
321369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
321469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
3215dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
3216bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId1);
3217bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId2);
3218127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3219127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3220127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3221127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3222b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3223b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
322470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
3225bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
32263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
32273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3228bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
3229f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
3230e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
3231627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
323249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
323370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
323470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
3235dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
3236743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
3237743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
3238743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
3239743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
3240e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
3241743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
3242743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
3243743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
3244743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
3245743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
324648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3247627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
3248743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
3249627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
3250627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
3251627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
325270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
325370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
325433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
3255e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
3256e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
3257e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
3258e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
3259e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3260e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
3261e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
3262e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
3263e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3264e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
3265e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
3266e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
3267e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
3268e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3269e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
3270e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3271e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
3272e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3273e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
3274e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3275e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
3276e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
3277e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
3278e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3279e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
3280e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
3281e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
3282d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
3283d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
3284d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
3285d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
32864458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
3287e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
3288e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
328933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
329033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
3291e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
329233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
329333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
329433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
329533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
329669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
329769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
329869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
329933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
330033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
330169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
330269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
330333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
330433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
330533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
330633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
330733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
330833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
330933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
331033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
331133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
3312bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                    mContactAggregator.updateAggregateData(mTransactionContext, contactId);
331333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
3314e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
3315bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                updateSearchIndexInTransaction();
331633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
331733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
3318e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
3319e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
3320e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
332170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
332270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
332370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
332470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
332573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
33263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
33273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
33283826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
33293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
33303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
33313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3332afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
333370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3334619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
33353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
33363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
33373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
33383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
33393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
33403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
33413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
33423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
33433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
33443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
33453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
33463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
33473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
33483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
33493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
33503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
33513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
33523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
33533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
33543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
335572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
3356bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
3357d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3358d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3359619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3360627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
3361627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
3362dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
3363743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
3364743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
3365743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
3366627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
3367627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
3368dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
3369627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
3370627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3371627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3372627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
3373627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
3374627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
3375627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
3376627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
33774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
33784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
33794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
338015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
338115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
338215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
3383d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
3384385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
33853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
33863716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1));
3387385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
33883716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
33893716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
33903716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                            Directory.DEFAULT));
3391d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
33923716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
33933716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
33943716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                            Directory.LOCAL_INVISIBLE));
3395d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3396d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3397d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
3398d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
3399a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
3400a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
3401d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3402d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3403d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
3404d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
3405d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
3406d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
3407d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
3408d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
3409d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3410d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
3411d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
3412d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
34132e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
34142e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
34152e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
34162e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
34172e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
34182e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
3419d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
342009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
342109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
342209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
342309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
342409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
3425332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
3426d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
34276ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
34286ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
34296ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
34306ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
34316ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
3432547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
3433547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
3434547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return wrapCursor(uri, cursor);
3435547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
3436547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return matrixCursorFromCursor(wrapCursor(uri, cursor));
3437547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
34383716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
34393716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
3440547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro    private Cursor wrapCursor(Uri uri, Cursor cursor) {
3441547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
3442547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
3443547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
3444547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return cursor;
3445547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
3446547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
34473716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
34483716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
34493716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
34503716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
34513716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
34523716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
34533716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
34543716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
34553716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
34563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
34573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
34583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
34593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
34603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
34613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
34623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
34633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
34643716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
3465547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        return new SnippetizingCursorWrapper(cursor, query, startMatch, endMatch, ellipsis,
3466547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro                maxTokens);
34676ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
34686ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
34696ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
34706ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
34716ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
34726ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
34736ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
34746ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
34756ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
34766ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
34776ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
34786ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
34796ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
34806ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
34816ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
34826ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
34836ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
34846ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
34856ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
34866ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
34876ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
34886ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
34896ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
3490332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
34916ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
3492d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3493d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3494d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
3495d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
3496d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
3497d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
3498d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
3499d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
3500d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
3501d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3502d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
3503d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
3504d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
3505d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
3506d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3507d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3508d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
3509d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
3510d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
3511d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
35124458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
35134458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
35144458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
351549d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
351649d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
35174458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
35184458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
35194458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
35204458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
35214458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
35224458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
35234458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
35244458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
35254458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
35264458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
35274458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
35284458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
35294458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
3530d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
35314458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
3532d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3533d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
35344458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
35354458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
3536d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3537d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
353872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
35394458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
35404458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
35414458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
354272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
354372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
3544d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    public Cursor queryLocal(Uri uri, String[] projection, String selection, String[] selectionArgs,
3545385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                String sortOrder, long directoryId) {
3546bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3547bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
3548bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
35490b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
3550b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
355135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3552d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
35531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
3554c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
3555c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3556619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
3557619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
3558a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
35594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
356035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3561b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
356235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
356335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3564d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3565763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
356624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean existingWhere = appendLocalDirectorySelectionIfNeeded(qb, directoryId);
356724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, Contacts.IS_USER_PROFILE, existingWhere);
356824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder);
3569619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
3570619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
3571619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3572d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
35734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
357424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForContact(contactId, false);
3575763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
35764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
35774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
35786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
35796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
35806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
35815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
35825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
35835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
35845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
35855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
3586fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3587fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
35885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
3589a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
35905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
35915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
35925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
359324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermissionForContact(contactId, false);
35945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3595763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
3596a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3597a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3598a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3599a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
3600a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
36015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
36025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
36035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
36045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3605763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
36064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
36074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
36084da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
36095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
36105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
36115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36122149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
36132149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
36142149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
36152149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
36162149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
36172149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
36182149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
36192149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
36202149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
36212149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
36222149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
362324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermissionForContact(contactId, false);
36242149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
36252149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
3626a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
3627a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3628a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3629a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
3630a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
36312149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
36322149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
36332149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
36342149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
36352149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
36362149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
36372149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
363824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
363924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForContact(contactId, false);
36402149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
364124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
36422149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
36432149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
36442149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
36452149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
3646f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
3647f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
364842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
364924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
365024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForContact(contactId, false);
3651763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
3652f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
36534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
365424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
36554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
3656f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
3657f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
3658f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
365942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
366042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
366142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
366242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
366342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
366442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
366542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
366642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
366742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
366842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
3669ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
3670916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
3671ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
3672916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
3673ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
36747ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
36757ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                        qb, uri, projection, filterParam, directoryId);
367624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, Contacts.IS_USER_PROFILE, false);
367724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder);
3678ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3679ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3680ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3681ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
3682ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
36834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3684ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3685d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
36864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
36874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3688e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
36895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
36904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
36914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
36924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3693763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3694ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
36955e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
36965e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
36975e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
3698dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    starredProjection =
3699dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
3700dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    frequentProjection =
3701dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
37025e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
370324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sStrequentStarredProjectionMap);
37045e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
37054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
37064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
370724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    qb.appendWhere(filterSql + " AND ");
3708d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
370924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
371024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                final String starredQuery = qb.buildQuery(starredProjection,
371124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
3712d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3713d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3714d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3715763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
371624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sStrequentFrequentProjectionMap);
37174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
371824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    qb.appendWhere(filterSql + " AND ");
3719d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
372024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
37215e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
3722d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3723d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
372424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts._ID, null, null, null);
3725d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3726d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3727d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3728d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
37294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
37304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3731d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3732d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3733d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3734d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3735d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3736d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3737ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3738763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3739b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
374071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
37414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3742b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3743b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3744b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3745b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
374624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
374724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
374824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
374924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
375024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
375124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
375224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
375324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
375424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
375524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
375624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Contacts.IS_USER_PROFILE + "=1");
375724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
375824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
375924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
376024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA: {
376124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
376224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
376324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
376424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
376524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
376624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
376724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA_ID: {
376824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
376924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
377024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
377124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Data._ID + "=? AND "
377224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
377324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
377424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
377524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
377624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
377724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
377824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // When reading as vCard always use restricted view
377924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
378024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
378124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
378224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
378324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
378424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3785a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
37864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
378782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
37894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
37906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
37916bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
379200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3793a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
37943653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
379582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
37974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
37983653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
37993653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
38003653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
38013653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
3802a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
3803a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
3804a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
3805a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
3806a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
3807a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
3808a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3809a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3810a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
3811a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
3812a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
3813a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
3814a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
3815a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3816a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
3817a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
3818a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
3819a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
3820a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
3821a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3822a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
3823a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
3824a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3825a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
3826a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
3827a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
3828a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
3829a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
3830a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
3831a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
3832a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
3833a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
3834a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
3835a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
3836a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
3837a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
3838a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
3839a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
3840a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
38414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
384282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
384389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
38442815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
38452815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
38462815f58f72f109790585931f601a63ddc02536a5Evan Millar
384748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
384882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
38494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
385048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
38514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
385248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
385348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
385448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3855ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
385646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
385746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
385846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
385946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
386046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
386146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
386289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3863ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
38644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
38654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3866a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
38675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
386845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
38695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
38705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
38715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
3872155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
3873155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
3874155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
3875155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
3876155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
3877155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
3878155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
38792352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
3880155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
38815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
388245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
38835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
38845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3885892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
3886892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
38875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
38885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
38895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
38905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
3891892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
3892892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
3893892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
3894892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
3895892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
389645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
389745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
389845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
389945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
390045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
390145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
390245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
39035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
39045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3905a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
3906ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
39075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3908a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
390946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
391046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
391146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
391246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
391346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
391446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
3915a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
3916ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3917ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3918ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
39194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
392082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
392189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
39224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
39234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
39244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
392548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
392682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
39274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
39284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
39294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
393048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
393148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
393248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
39335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
393482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
393589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
39364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
393708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
393808768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
393908768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
394008768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
39414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3942ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3943ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3944ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
39455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
394646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
394746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
394846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
394946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
395046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
395146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
395207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
39537d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
395407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
395507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
395607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
395707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
395807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
395907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
39605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
396107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
396207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
396307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
396407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
396507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
396607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
396707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
396807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
396907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
39702a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
39712a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(mDbHelper.getMimeTypeIdForEmail());
39722a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
397307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
397420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
3975155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
3976155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
3977155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
3978155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
3979155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(mDbHelper.getMimeTypeIdForEmail());
3980155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
3981155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
3982155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
3983155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
3984155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
3985155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
3986155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
39872352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
3988155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
39895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
39905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3991a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
39925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
39935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
3994a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
399546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
399646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
399746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
39987d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
39997d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
40007d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
4001a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
40025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
40035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
40045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4005ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
400682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
400789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
400889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4009ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4010ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4011ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
401248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
401382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
40144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
401548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
401648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
40174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
401848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
401948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
402048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
40215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4022763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
402324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true);
40244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
40254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
40264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
40285ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
402924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, false);
4030763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
40314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
40324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
40334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
40344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
40354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40365ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
40375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
403882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
40394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
40404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
404124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true);
404224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
404324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
404424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
404524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
404624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
404724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
404824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
404924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
405024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
405124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
405224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
405324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID: {
405424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
405524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = ContentUris.parseId(uri);
405624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
405724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
405824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
405924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
406024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
406124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
406224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
406324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
406424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
406524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
406624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
406724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
406824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
406924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + Data.RAW_CONTACT_ID + "=?");
407024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
407124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
407224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
407324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
407424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
407524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
407624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
407724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
407824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
407924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
4080e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4081e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4082e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
4083e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
408482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
408524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true);
4086e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4087e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4088e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
40894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
409024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = ContentUris.parseId(uri);
409124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForData(dataId, false);
409282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
40934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
40944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
4095a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
4096a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
4097a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
4098a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
40994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4100a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
4101a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
4102a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
4103892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
4104a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
4105a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4106e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
4107e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
4108e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        mDbHelper.getCurrentCountryIso());
4109892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
4110892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
4111892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
4112e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
4113e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
4114e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
4115e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
4116a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
4117a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
4118a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4119ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
4120b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4121ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
412289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4123ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4124ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4125ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4126ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
4127b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4128ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
41294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
41304da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
4131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4132ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4133ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4134ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
4135b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
4136ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
413789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
413889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
4139ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4140ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4141ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4142b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
41430c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
4144b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
4145b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
4146b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
4147b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
414831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
4149d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
41502d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
41512d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
41522d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
41532d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
415431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
4155d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
4156d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
415731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
415831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
415931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
416031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
41615b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
41625b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
41635b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
41645b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
41655b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
41665b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
41675b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
41685b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
416976dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
41705b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
41715b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
41725b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
41735b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
41745b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
41755b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
41765b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
4177763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
41787581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
41797581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
41805b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
418131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
418231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4183eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
4184eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
4185eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
418689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4187e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4188e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
4189e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
4190b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
4191e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
419282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4193b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
4194e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4195e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
419682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4197b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
4198e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4199e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
4200e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4201eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
4202eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
4203eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
420482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
42050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
42065ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
42075ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
42085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
420982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
42100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
42114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
42124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
42135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
42145ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
42155ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
4216c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
4217174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
4218174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, uri, projection, limit);
4219c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4220c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4221c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
42222d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
4223174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
4224174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
4225174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
4226174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, projection, lookupKey, filter);
4227c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4228c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
42291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
4230b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
42311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
42321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
42331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
42341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
4235b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
42361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
42371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
42381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
42391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
42401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
4241b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
42421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
42431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
42441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
42451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
42461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
4247b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
42481b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
424971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
42501b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
42511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
42521b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
425346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
4254a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
425546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
425646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
425746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
425846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
425946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
4260a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
42614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
42624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
426346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
426446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
426546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
426609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
426709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
426809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
426909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4270d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
4271d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4272d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4273d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4274d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4275d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4276d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
4277385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
4278d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4279d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4280385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
4281d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
4282d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4283d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4284d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
42857a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
42867a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
42877a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
42887a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
42894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
4290f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
4291c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
42924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
42934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
429409e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
42957f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
4296ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
4297ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
4298ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
4299ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
4300ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4301ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
43025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
43035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
43045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
43055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
43065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
4307038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
4308038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
4309038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
4310038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
43115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
43125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
43134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
43144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
43154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
43164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
43174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
43184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
431909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
432009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
432109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
432209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
432309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
432409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
432509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
432609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
432709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
432809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
432909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
433009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
433109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
433209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
433309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
433409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4335a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
4336a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
4337a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
4338a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
4339a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
4340a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
4341a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
4342a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
4343a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
4344a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
4345a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
4346a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
4347a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
4348a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
4349a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
4350a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
4351a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4352a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
4353a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
4354a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
4355a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
4356a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
4357a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
4358a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
4359a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4360a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4361a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
4362a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
4363a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
436409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4365bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
4366bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
4367bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
4368bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
4369ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4370bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4371bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
4372ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
4373ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4374bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
4375bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
4376bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
4377bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
437824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The first letter of the sort key column is what is used for the index headings, except
437924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // in the case of the user's profile, in which case it is empty.
438024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        public static final String SECTION_HEADING_TEMPLATE =
438124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "(CASE WHEN %1$s=1 THEN '' ELSE SUBSTR(%2$s,1,1) END)";
438224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4383de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
4384ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4385ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4386ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
4387ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
4388ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
4389ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
4390ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
4391ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
4392ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
4393ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4394ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
4395ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
4396ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
4397ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
4398ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
439924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
440024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // If the sort order contains one of the "is_profile" columns, we need to strip it out
440124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // first.
440224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (sortOrder.contains(Contacts.IS_USER_PROFILE)
440324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    || sortOrder.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
440424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                String[] splitOrderClauses = sortOrder.split(",");
440524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder rejoinedClause = new StringBuilder();
440624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                for (String orderClause : splitOrderClauses) {
440724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!orderClause.contains(Contacts.IS_USER_PROFILE)
440824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            && !orderClause.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
440924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (rejoinedClause.length() > 0) {
441024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            rejoinedClause.append(", ");
441124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
441224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        rejoinedClause.append(orderClause.trim());
441324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
441424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
441524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = rejoinedClause.toString();
441624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
441724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4418ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
4419ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
4420ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
4421ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
4422ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
4423ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
4424ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4425ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
4426ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
4427ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4428ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4429bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
4430ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
443124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
443224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user profile column varies depending on the view.
443324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String profileColumn = qb.getTables().contains(mDbHelper.getContactView())
443424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? Contacts.IS_USER_PROFILE
443524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : RawContacts.RAW_CONTACT_IS_USER_PROFILE;
443624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String sectionHeading = String.format(
443724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                AddressBookIndexQuery.SECTION_HEADING_TEMPLATE, profileColumn, sortKey);
4438bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
443924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
4440bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4441bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
4442bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
4443bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
4444bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
4445bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
4446bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
4447bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
4448ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
444924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
4450bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
4451ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
4452ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
4453ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
4454ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4455f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
4456ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
4457ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
4458ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4459ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
4460f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
4461ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
4462ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
4463bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
4464bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
4465bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4466bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
4467bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
4468bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
4469ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
4470f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
4471bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
4472bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
4473bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
4474bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
4475bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
4476bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
4477bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
4478bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
4479bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
4480bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
4481bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4482bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
4483bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
4484bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
4485bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
4486bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4487bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
4488bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
4489bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
4490ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4491ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4492e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return new AddressBookCursor((CrossProcessCursor) cursor, titles, counts);
4493ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
4494f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
4495ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4496ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4497ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
44982d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
449992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
450092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
450192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
450292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
45032d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
45042d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
45055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
45065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
45075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
450892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
450992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
451092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
451192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
451292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
451392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
451492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
451592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
451692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
451792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
451892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
451992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
452092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
452192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
452292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
452392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
452492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
452592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
452692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
45275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
45285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
45295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
45315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
45325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
45345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
45355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
45375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
45385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
45395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
45405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
45415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
45425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
45445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
45455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
45465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
45475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
45485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
45505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
45515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
45525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
45535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
45545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
455592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
45565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
45575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
45585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
45595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
45605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
45615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
45625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
45645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
45655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
45665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
45675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
45685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
45695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
45705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
45715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
45725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
45735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
457492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
457592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
45765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
45775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
45785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
45795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
45805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
45815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
45825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
45835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
45845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
45855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
45875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
45885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
458992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
459092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
45915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
45935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
45945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
45955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
459692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
45975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
45985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
45995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
46005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
46015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
460292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
46035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
46045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
460592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
460692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
460792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
460892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
46095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
46105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
461192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
461292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
461392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
46145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
46155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
461692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
461792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
46185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
461992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
462092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
462192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
462292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
462392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
462492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
462592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
462692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
462792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
462892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
462992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
463092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
463192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
463292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
463392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
463492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
463592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
463692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
463792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
463892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
463992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
46405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
46415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
464292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
464392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
464492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
464592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
464692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
464792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
464892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
464992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
465092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
465192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
465292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
465392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
465492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
465592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
465692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
465792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
465892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
465992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
466092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
466192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
466292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
46635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
46645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
46655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
46665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
466792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
466892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
46695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
46705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
46715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
46725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
46735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
46745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
46755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
46765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
46775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
46785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
46795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
46805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
46815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
46825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
46835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
46845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
46855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
46865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
46875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
468892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
468992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
469092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
46915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
46925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
46935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
46945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
46955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
46965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
46975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
46985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
46995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
47005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
47025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
47035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
470492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
470592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
470692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
470792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
470892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
470992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
471092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
471192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
471292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
471392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
471492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
4715ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
4716ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
4717ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
4718ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
47195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
47205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
47215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
47225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
47235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
47245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
47265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
47275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
47295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
47305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
47325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
47335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
47345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
47355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
47365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
47375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
47385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
47395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
47405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
47415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
47425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
47435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
47445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
47455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
47465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
47475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
47485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
47495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
47505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
47515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
47525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
47535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4754763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
4755763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
475682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
47577ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        sb.append(mDbHelper.getContactView(shouldExcludeRestrictedData(uri)));
47587ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
47597ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
4760916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
4761916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
4762916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
4763916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4764916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
4765916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
4766916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
4767916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
4768916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
47697ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            String[] projection, String filter, long directoryId) {
47707ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
47717ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
47727ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        sb.append(mDbHelper.getContactView(shouldExcludeRestrictedData(uri)));
4773916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
477403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
477503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
477603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
477703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
477830cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
477930cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
47805e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
47815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            appendSearchIndexJoin(sb, uri, projection, filter);
47825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
47837ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
47847ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
478503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
478603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
478703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
4788916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
478903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
479003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            StringBuilder sb, Uri uri, String[] projection, String filter) {
4791916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
4792174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET)) {
479303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
479403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
479503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
479603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
479703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
479803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
479903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
48005e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
48015e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
48025e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
48035e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
48045e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
48055e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
48065e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
48075e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
48085e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
4809174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
4810174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens);
4811174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
4812174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(sb, filter, false, null, null, null, 0);
4813174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
4814174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
4815174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
4816174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
4817174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
4818174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            int maxTokens) {
4819174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
4820174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
4821174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
4822174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
4823174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
4824174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
48253716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
48263716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
48273716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        boolean singleTokenSearch = filter.split(QUERY_TOKENIZER_REGEX).length == 1;
48283716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4829174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
4830174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            emailAddress = mDbHelper.extractAddressFromEmailAddress(filter);
4831174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
4832174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
4833174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
483404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
483504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
483604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
483704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        mDbHelper.getCountryIso());
483804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
4839174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
4840174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
4841174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
4842174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
48435e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
48445e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
48453d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
48465e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
484704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
484804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
484904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
485004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
485104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
485204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
48533d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
48543d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
48553716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
48563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
48573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
48583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
48593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
48603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
48613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
48623d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
48633d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
48643d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
48653d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
486604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
486704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
486804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
486904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
487004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
487104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
487204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
487304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
487404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
487504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
487604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
487704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
487804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
487904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
488004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
488104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
48825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
48835e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
48843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
48853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
48863716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
48873716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
48883716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
48893716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
48903716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
48915e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
489203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
489304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
489404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
48953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    // Optimization for single-token search.
48963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    if (singleTokenSearch) {
48973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
48983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
48993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
49003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
49013716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
49023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
49033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
49043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
49053716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
49063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
49073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
49083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
49093716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
49103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
49113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
49123716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
491304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
491404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
491504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
491603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
49175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
49185e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
491903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
49205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
49215e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
49225e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(Tables.SEARCH_INDEX + " MATCH ");
49235e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
49242352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, "\"" + sanitizeMatch(filter) + "*\"");
49253d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
49262352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb,
492704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    "\"" + sanitizeMatch(filter) + "*\" OR \"" + phoneNumber + "*\""
49282352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                            + (numberE164 != null ? " OR \"" + numberE164 + "\"" : ""));
492903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
49302352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filter) + "*");
49319c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
493203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
4933a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
4934a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
49352352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    private String sanitizeMatch(String filter) {
49362352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        // TODO more robust preprocessing of match expressions
49372352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        return filter.replace('-', ' ').replace('\"', ' ');
49382352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
49392352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
49405e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
49415e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
49425e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
49435e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
49445e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
49455e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
49465e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
49475e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
49485e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
49495e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
49505e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
49515e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
49525e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
49535e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
49545e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
4955763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
4956763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
49577ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        sb.append(mDbHelper.getRawContactView(shouldExcludeRestrictedData(uri)));
4958763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
4959763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
4960763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
4961763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
4962763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4963a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
4964a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(mDbHelper.getRawEntitiesView(shouldExcludeRestrictedData(uri)));
4965a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
496646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
496746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
496846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
496982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
497082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
497146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
497246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
497346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
497446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
497546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
497646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
497746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
497846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
497946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
498082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4981a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getDataView(shouldExcludeRestrictedData(uri)));
498282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
498382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
4984a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
4985a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
4986a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
4987a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
49883296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
498946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
499046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
499146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
499246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
499382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
4994f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
4995f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
4996f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
4997f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
4998f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
499982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
5000ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
5001ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
50020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
50030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
50040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5005b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
50060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
5007a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5008a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
50090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5010a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5011a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
5012a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5013a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5014a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
5015a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
5016a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5017a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getEntitiesView(shouldExcludeRestrictedData(uri)));
5018a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
5019a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5020a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
5021a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5022a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
5023a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
5024a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5025a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5026a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
5027a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
5028a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5029a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5030a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
5031a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
5032a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5033a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
5034a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
5035a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
5036a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
5037a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
5038a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
5039a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
5040a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
5041a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
50420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5043a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
50440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5045a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
5046a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5047b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
50480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
50490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
50500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
50510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
50520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
50530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
5054a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
5055a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
50560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5057a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5058a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
505946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
506046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
506146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
506246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
506346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
506446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
5065a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
5066a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
5067a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5068a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
5069a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
5070a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
5071a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
5072a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5073a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5074a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5075a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
5076a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5077a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
5078a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
5079a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
5080a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5081a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5082a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
508324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
5084385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
5085385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
508624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
5087385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
5088385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
508924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
509024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
509124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
509224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
509324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
509424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void appendProfileRestriction(SQLiteQueryBuilder qb, Uri uri, String profileColumn,
509524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            boolean andRequired) {
509624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (!shouldIncludeProfile(uri)) {
509724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            qb.appendWhere((andRequired ? " AND (" : "")
509824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + profileColumn + " IS NULL OR "
509924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + profileColumn + "=0"
510024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + (andRequired ? ")" : ""));
5101385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
5102385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
5103385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
510424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private String prependProfileSortIfNeeded(Uri uri, String sortOrder) {
510524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (shouldIncludeProfile(uri)) {
510624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (TextUtils.isEmpty(sortOrder)) {
510724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC";
510824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            } else {
510924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC, " + sortOrder;
511024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
511124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
511224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return sortOrder;
511324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
511424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
511524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean shouldIncludeProfile(Uri uri) {
511624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user's profile may be returned alongside other contacts if it was requested and
511724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // the calling application has permission to read profile data.
511824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean profileRequested = readBooleanQueryParameter(uri, ContactsContract.INCLUDE_PROFILE,
511924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                false);
512024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (profileRequested) {
512124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(false);
512224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
512324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return profileRequested;
512424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
512524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5126a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private boolean shouldExcludeRestrictedData(Uri uri) {
5127a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        // Note: currently, "export only" equals to "restricted", but may not in the future.
5128a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
5129a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.FOR_EXPORT_ONLY, false);
5130a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (excludeRestrictedData) {
5131a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return true;
5132a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5133a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5134a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5135a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5136a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (requestingPackage != null) {
5137a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5138a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5139a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5140a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return false;
51410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
51420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
51434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
5144f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5145f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5146e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5147e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5148e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5149e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5150fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5151fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5152e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5153e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5154e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5155e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5156e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5157e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
51584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
51594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
51604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
51614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
51624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
51634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
51644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
51654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
51664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5167e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
5168f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5169f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5170e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5171e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5172e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5173e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5174fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5175fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5176e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5177e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5178e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5179e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5180e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5181e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
5182e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
5183e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
5184e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
5185e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
5186e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
5187e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
5188e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
5189e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
5190e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
5191e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
5192e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
5193e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
5194e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
5195e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
5196e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
51977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5198c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
5199c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
5200c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
5201c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
5202c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
5203f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
52042e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
5205c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
5206c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5207c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5208c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
5209c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
5210c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
5211c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
5212c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
5213c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
5214c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5215c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
5216c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
5217c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
5218c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5219c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5220c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5221c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
522200ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
5223d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
522470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
522570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
5226fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
522770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
522870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
522970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
523070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
5231d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
523270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
523367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
52345ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
52355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
5236619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
5237619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
5238619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5239b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
5240f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
5241415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
5242415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
5243415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
5244b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
5245b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
5246a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
524724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
524824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, false);
5249f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
525024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
525124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
525224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(rawContactId)});
5253e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
5254b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5255e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
525624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
525724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForData(dataId, false);
5258f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
5259e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
526024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
5261d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5262d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5263fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
5264fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
5265fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
5266fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
5267fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5268fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
5269fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
5270fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
527142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
5272fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
527342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
527442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
527542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
527642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5277fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
5278f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
527942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
528042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
528142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
528249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
528342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
528442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
528542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
5286fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
528742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
5288fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
5289fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                mProfileIdCache.init(mDb, false);
5290fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
5291d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
5292d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
529342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
529442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
5295d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
529642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
5297d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
529842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
529924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    long contactId = lookupContactIdByLookupKey(db, lookupKey);
530024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermissionForContact(contactId, false);
530124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
5302fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    if (mProfileIdCache.profileContactId == contactId) {
5303fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                        queryUri = queryUri.buildUpon().appendQueryParameter(
5304fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                                ContactsContract.INCLUDE_PROFILE, "true").build();
5305fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    }
530642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
530742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
530842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
530942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
5310d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5311d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
5312d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
5313d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
5314d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5315fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
5316f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
5317d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5318b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5319b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
5320fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
5321fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
5322b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
5323b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
5324b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5325f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,
5326e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
5327e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
5328e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
5329e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
5330e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
5331e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
5332e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5333e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
5334e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
5335e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
5336e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
533708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
5338f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
5339f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
534008ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
534108ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
534208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
534308ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
5344e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
5345e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5346d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
5347d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5348d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5349f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
5350d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
5351d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5352f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
5353d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
5354d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
5355d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5356d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
5357d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5358f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
5359f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
5360f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
5361d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
5362ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
5363ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
5364d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5365d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5366d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5367f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
5368f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
5369f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
5370f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
5371f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
5372f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
5373f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
5374f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
5375d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5376d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
5377d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
5378d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
5379d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5380fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
5381fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
5382d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
5383dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
5384fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
5385fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
5386dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
5387dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
53887a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
5389dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
5390108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
5391108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
5392fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            // No extra checks since composer always uses restricted views.
5393108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
5394fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null)) {
5395108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
5396108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
5397108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
5398d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5399108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
5400108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
5401108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
5402108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
5403108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
5404108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
5405108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
5406108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
5407108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
5408108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
5409108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
5410108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
5411108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
5412d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5413d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5414d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5415b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
54164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
54174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
5418415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
5419415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
5420415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
5421a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
54224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
5423b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
5424be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
54252d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
5426b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
5427b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
542824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
5429b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
5430f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
543142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
543224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
5433f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
5434f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
5435f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                return "image/png";
5436b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
543724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
5438be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
5439b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
544024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
5441b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
5442f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
544324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
5444f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
5445508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
5446b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
544748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
544848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
544948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
545048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
54519005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
54529005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
545348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
545448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
545548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
545648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
545748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
545848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
545948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
546048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
5461b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
5462b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
5463b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
5464b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
5465b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
5466b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
5467b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
5468b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
5469c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
5470c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
5471c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
5472c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
5473d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
5474d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
5475d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
5476d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
547761efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
547861efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
54794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
54804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
54817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
548209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
548309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
548409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
548509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
548609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
548709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
548809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
548909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
549024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
549109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
549209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
54938727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
549424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
54958727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
54968727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
549709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
549809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
549924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
550009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
550109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
550209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
550309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
550424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
550524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
550609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
550709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
550809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
550909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
551009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
551109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
551209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
551309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
551409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
551524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
551609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
551709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
551809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
551909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
552009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
552109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
552209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
552309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
552409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
552509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
552609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
552709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
552809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
552909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
553009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
553109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
553209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
553309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
553409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
553509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
553609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
5537f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
5538f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5539f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
5540f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
5541f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5542f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5543f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5544f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
5545f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
554678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.insertNameLookup(rawContactId, dataId, lookupType, name);
5547f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5548f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5549f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5550f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
5551d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
5552f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5553f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5554f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
55552d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
5556d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5557d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
5558d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
5559d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
5560d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
5561d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
5562d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
5563e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
5564916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
5565916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
5566e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
5567e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
55689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
55699a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
55709a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
55719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
55729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
55739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
55749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
55759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
55769a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
55779a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
55789a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
55799a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
55809a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
55819a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
55829a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
55834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
55847a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
55857a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
55867a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
55877a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
55887a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
55897a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
55907a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
55917a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
55927a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
55937a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
5594f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
5595f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
55967a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
55977a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
55987a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
55997a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
56007a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
56017a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
56027a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
56037a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
56047a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
56057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
56067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
56077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
56087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
56097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
56107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
56117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
56127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
56137a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
56147a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
56157a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
56167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
56177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
56187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
56197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
56207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
56217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
56227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
56237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
56247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
56257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
56267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
56277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
56287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
56297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
56307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
56317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
56324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
56334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
56344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5635b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
5636b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
5637b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
5638b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
5639b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
56404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
56414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5642b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
5643b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5644b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5645caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
56465e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
56475e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
56485e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
56495e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
56505e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
56515e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
56525e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
56535e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
56545e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
56555e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
56565e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5657caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
5658caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
5659caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
56605f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
5661caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
5662caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
5663caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
5664caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
56656f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
5666caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
56676f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
5668caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
5669f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
567073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
567173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
567273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
567373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
5674bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
5675bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
5676bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
5677bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
567873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
567973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
568073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
568173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
568273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
5683627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
5684627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
5685627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
5686627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
568773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
568873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
568973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
5690627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
5691627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
5692627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
5693627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
5694627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
569573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
569673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
569773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
569873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
569973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
570073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
570173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
5702627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
5703b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
5704d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5705f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
5706f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
5707f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5708f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
5709f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5710f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5711f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5712f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5713f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5714f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
5715f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
5716f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5717f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5718f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5719f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
5720f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5721f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
5722f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
5723f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5724f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5725f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
5726f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
5727f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
5728f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
5729f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
5730f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5731f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5732f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
5733f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
5734f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
5735f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
5736f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5737f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5738f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
5739f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5740f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5741f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
5742f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
5743f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5744f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
5745f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
5746f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
5747f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
5748f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
5749f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
57505fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
57515fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
57525fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
57535fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
57545fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
57555fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
57565fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
57575fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
57585fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
57595fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
57605fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
5761f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5762f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5763f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
5764f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5765f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
5766f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5767f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5768f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5769f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
5770f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
5771f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
5772f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5773f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5774f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5775f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
5776f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
5777f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
5778f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
5779f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
5780f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5781f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5782f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
5783f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
57845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
57850dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
57860dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
57870dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
57880dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
57890dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
57900dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
57910dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
57920dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
57930dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
5794bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
57950dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
57960dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
57970dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
57980dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
57990dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
58000dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
58010dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
580249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = mDbHelper.getWritableDatabase();
58030dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
58040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
58050dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
58060dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
58070dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
58080dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
58090dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
58100dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE,
58110dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
58120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
58130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
58140dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
58150dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
58160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
58170dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
58180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
58190dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
58200dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
58210dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
5822bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
5823bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
58240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
58250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
58260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
58270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
58280dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
58290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
58300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
58310dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
58320dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
58330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
58349a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
58359a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
58369a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
58379a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
58389a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
58399a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
58409a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
58419a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
58429a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
584346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
584446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
584546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
584646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
584746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
584846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
584946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
585046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
585146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
585246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
585346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
585446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
585546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
585646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
585746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
585846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
585946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
586046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
586146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
586246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
586346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
586446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
586546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final Cursor cursor = mDb.query(
586646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDbHelper.getDataView(shouldExcludeRestrictedData(uri)),
586746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
586846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
586946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
587046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
587146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
587246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
587346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
587446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.update(Tables.CONTACTS, values2, Contacts._ID + "=?", mSelectionArgs1);
587546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
587646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
587746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
587846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
587946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
588046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
588146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
588246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
588346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
588446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
588546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
588646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
588746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
588846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
588946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
589046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private int updateDataUsageStat(
589146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            ArrayList<Long> dataIds, String type, long currentTimeMillis) {
589246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
589346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
589446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
589546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
589646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
589746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
589846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
589946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
590046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            mDb.beginTransaction();
590146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
590246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                final Cursor cursor = mDb.query(Tables.DATA_USAGE_STAT, columns, where, args,
590346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        null, null, null);
590446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
590546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
590646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
590746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
590846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
590946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
591046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
591146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
591246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
591346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            mDb.update(Tables.DATA_USAGE_STAT, values,
591446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
591546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
591646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
591746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
591846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
591946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
592046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
592146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
592246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
592346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        mDb.insert(Tables.DATA_USAGE_STAT, null, values);
592446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
592546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    mDb.setTransactionSuccessful();
592646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
592746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
592846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
592946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
593046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.endTransaction();
593146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
593246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
593346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
593446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
593546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
593646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
593746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
593846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
593946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
594046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
594146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
594246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
594346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
594446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
594546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
594646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
594746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
594846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
594946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
595046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
595146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
595246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
595346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
595446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
595546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
595646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
595746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
595846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
595946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
596046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
596146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
596246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
596346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
596446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
59654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
5966