ContactsProvider2.java revision ed6bfd922fd84db21de08c1d12e93c501b86560d
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;
322f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawaimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
3797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
3903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
4097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
4197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.StreamItemsColumns;
433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.StreamItemPhotosColumns;
4497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
45ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.Views;
462f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawaimport com.android.providers.contacts.util.DbQueryUtils;
4797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardComposer;
4897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardConfig;
4997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
5097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
5197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
5297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
53b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
54caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
555b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
56bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.Notification;
57bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.NotificationManager;
58bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.PendingIntent;
59c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
60568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
61568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
626ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
6335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
6467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
6567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
66627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
67bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.Intent;
68568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
70627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
7167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
72f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.content.res.Resources;
74e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CrossProcessCursor;
754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
76e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CursorWindow;
77ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
78ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
7909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
8009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
8208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
85d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
86bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Binder;
876ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
88bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Handler;
89bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.HandlerThread;
90bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Message;
91ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
92bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Process;
93b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
9415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.os.StrictMode;
950dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
960e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
973d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
98508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
993de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
100b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
10197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
10297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
10397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
10497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
1056d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Note;
10697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
10797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
10897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
10997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
11097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
111ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
1123de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1135b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1143de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
11571340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport android.provider.ContactsContract.DataUsageFeedback;
116d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
1173de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
118bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1193de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
12009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1213de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
122916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1233de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
12482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
1253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.provider.ContactsContract.StreamItems;
1263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.provider.ContactsContract.StreamItemPhotos;
12797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
12897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
12997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
130a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
1319a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport android.telephony.TelephonyManager;
132a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
133c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
135108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.BufferedWriter;
136d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
137b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
138d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
139d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
140108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.OutputStreamWriter;
141108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.Writer;
14242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
14446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport java.util.Arrays;
1455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
14642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
147b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1480e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
150622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
151b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1520e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
153ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1595b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
160caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
161bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
162bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
163bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
16515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_INITIALIZE = 0;
16615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
16715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS = 2;
16815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
16915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
17015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
17105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
17205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
17305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
17405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
175619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Limit for the maximum number of social stream items to store under a raw contact. */
1803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int MAX_STREAM_ITEMS_PER_RAW_CONTACT = 5;
1813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
183b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1843d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1853d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1863d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
187b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
188b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
18951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
1903d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1910dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
1920dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
1930dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
1940e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1950e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
196a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1982f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
1992f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * Used to insert a column into strequent results, which enables SQL to sort the list using
2002f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * the total times contacted. See also {@link #sStrequentFrequentProjectionMap}.
2012f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
2022f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private static final String TIMES_USED_SORT_COLUMN = "times_used_sort";
2035e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
204d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
2052f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            + TIMES_USED_SORT_COLUMN + " DESC, "
2069b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
207d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
208d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
209d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
210d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
2116e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
2129b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
2139b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2149b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
2159b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2166e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2179b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
2189b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2199b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
2209b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
221de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
222de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2233716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2243716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2253716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2263716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
227d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
228d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
231a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
236a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
237f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
23842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private static final int CONTACTS_AS_MULTI_VCARD = 1011;
2392149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_DATA = 1012;
2402149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_DATA = 1013;
241a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_ENTITIES = 1014;
242a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ENTITIES = 1015;
243a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1016;
2443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int CONTACTS_ID_STREAM_ITEMS = 1017;
2453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int CONTACTS_LOOKUP_STREAM_ITEMS = 1018;
2463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int CONTACTS_LOOKUP_ID_STREAM_ITEMS = 1019;
2474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2485ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2495ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
25146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
2523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2006;
2534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2546bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2556bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
256ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
25748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
25848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
25948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
26048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
26148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
26248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
26348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
26448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
265a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2666bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2676bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
268b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
269b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
270b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
27182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
27282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
27431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
27531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
276eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
277eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
278ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
279ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
280ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
281ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
28235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
283b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
28435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
285c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
286c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
287c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2881b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2891b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2901b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2911b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2921b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
29346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
29446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
29509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
29609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
297d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
298d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
299d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3007a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
3017a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
30224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
30324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
30424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
30524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
30624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
30724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
30824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
30924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
31024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
31124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
31246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
31346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
3143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS = 21000;
3153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_PHOTOS = 21001;
3163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID = 21002;
3173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS = 21003;
3183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
3193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_LIMIT = 21005;
3203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
321dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
322dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
323dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
324dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
325dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
326dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE
327dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
328dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
329dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
330dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
331dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
332dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
333dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
334dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND "
335dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + Groups.AUTO_ADD + " != 0";
336dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
337dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
338dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
339dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
340dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
341dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
342dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
343dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
344dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
345dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
346dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
347dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
348e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    public class AddressBookCursor extends CursorWrapper implements CrossProcessCursor {
349e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final CrossProcessCursor mCursor;
350e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        private final Bundle mBundle;
351e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
352e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public AddressBookCursor(CrossProcessCursor cursor, String[] titles, int[] counts) {
353e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            super(cursor);
354e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor = cursor;
355e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle = new Bundle();
356e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
357e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mBundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
358e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
359e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
360e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
361e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public Bundle getExtras() {
362e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mBundle;
363e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
364e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
365e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
366e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public void fillWindow(int pos, CursorWindow window) {
367e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            mCursor.fillWindow(pos, window);
368e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
369e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
370e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
371e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public CursorWindow getWindow() {
372e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.getWindow();
373e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
374e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
375e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        @Override
376e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        public boolean onMove(int oldPosition, int newPosition) {
377e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return mCursor.onMove(oldPosition, newPosition);
378e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov        }
379e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov    }
380e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov
381d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
382f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
383f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
384f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
38567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
38667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
3876cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
3883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
389f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
390ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
391ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
392d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
39367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
394d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
395ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
397f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
39819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
39919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
40019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
401ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
402ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
403ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
40419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
40519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
40619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
407ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
408ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
40919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
41019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
411c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
412caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
41371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
41471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
41571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
41671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
41771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
41871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
41971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
42071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
42171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
42271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
42371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
42471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
42571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
42671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
427a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
428a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
429a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
430a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
431a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
432a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
433a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
434a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
435a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
436a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
437a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
438a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
439c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
440c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
441c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
442c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
443c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
444c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
445c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
446c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
4472262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * second in_visible_group, then the rest.
4482262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
4492262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly
450c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
45146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
45246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
453c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
454c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
4552262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
4562262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
45746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + "(CASE WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_CURRENT
45846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
45946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + " WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_RECENT
46046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
46146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
46246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
46346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
46446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
465c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
466c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
46746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
46846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
46946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
470c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
471916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
472916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
473916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
474916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
47592ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
476916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
477f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
478f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
479f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
480f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
481f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
482f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
483f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
484f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
485f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
486f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
487f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
488f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
489f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
490f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
491f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
492916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
493f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
494f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
495f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
496f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
497f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
498f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
499f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
500f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
501f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
502f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
503f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
5043d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
5053d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
506f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
507f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
508f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
509f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
510f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
511cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
512f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
513f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
514f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
515f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
516f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
517f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
518f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
519f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
520f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
521f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
522f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
523f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
524f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
525f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
53203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
533f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
606038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
611e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
61624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
621916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
626916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6275e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6302f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6352f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
638f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
640fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
645ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
66324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
668a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
67524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
681a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
68924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
701f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
70424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
705f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
709f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
710f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
71624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
717f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
7333d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
7343d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
7412530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
744ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
762c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
769ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ")")
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Contacts.HAS_PHONE_NUMBER + ")")
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
786373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
794eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
817f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
821f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
823f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
824f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
825f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
826f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
827f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
828f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
829f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
830f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
831f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
83282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
833f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
834f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
835f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
838f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
839f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
840f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
841f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
844f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
845f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
846f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
847f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
848f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
849f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
850f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
851f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
852f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
853f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
8543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
8553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
8563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems._ID, StreamItemsColumns.CONCRETE_ID)
8573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(RawContacts.CONTACT_ID)
8583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
8593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
8603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
8613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
8623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
8633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
8643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
8653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.ACTION)
8663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.ACTION_URI)
8673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
8683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
8693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
8703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
8713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
8723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
8733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
8743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.PICTURE)
8753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.ACTION, StreamItemPhotosColumns.CONCRETE_ACTION)
8763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.ACTION_URI, StreamItemPhotosColumns.CONCRETE_ACTION_URI)
8773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
8783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
8791b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
880f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
881f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
882f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
883f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
884f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
885f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
886f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
887f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
888d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
889f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
890f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
891f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
892f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
893f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
894f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
895f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
896f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
897f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
898778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
899778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
900f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
9017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
9029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
9039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
9049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
9059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
9069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
9079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
9082526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
9092526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
910bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
911bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
912bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
913bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
91451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
91503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
91603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
91703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
91803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
91903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
9209a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
9219a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
9229a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
923f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
9241129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
9251129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
9262526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
9272526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
928f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
929f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
93046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
93146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
93246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
93346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
93446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
93546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
9364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
9374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
938a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
939d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
940d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
941a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
942a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
9433653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
9443653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
9452d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
9462d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
947a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
9483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
9493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
950c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
9515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
9525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
9532149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
9545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
9552149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
9562149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
957a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
958a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
959a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
960a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
9613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
9623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
9633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
9643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
965f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
96642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
96742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
9685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
969ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
970ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
9715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
9723653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
9735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
9745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
9755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
97646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
9773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
9783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
97946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
98046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
981b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
9824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
9834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
984ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
98548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
9865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
987ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
9884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
98948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
9901dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
9915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
9925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
9934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
994ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
99548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
99646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
99746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
9981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
999ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1000ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1001ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1002ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
100335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1004b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1005b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
100635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1007a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1008b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1009b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1010b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1011b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
10124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1013eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1014eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
101582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
101682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
10171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1018c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1019c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1020c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1021c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
10222d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1023c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1024c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
10251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
10261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
10271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
10281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
10291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
10301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
10311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
10321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
103309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
103409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1035d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1036d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1037d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
10387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
10397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
104024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
104124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
104224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
104324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
104424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
104524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
104624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
104724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
104824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
104924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
105024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
105124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
105224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
105346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
10543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
10553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
10563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
10573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
10583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
10593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
10603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
10613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
106246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
106346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
106446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
106546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
106646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
106746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
106846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
106919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
107019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1071d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1072d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1073d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1074d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1075d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1076d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1077d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1078d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1079d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
10804458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
10814458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1082d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
10833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
1084ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1085ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1086ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1087e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1088ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1089ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1090ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1091ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1092ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1093a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1094e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1095e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1096e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1097e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1098e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
109924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
110024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Cached information about the contact ID and raw contact IDs that make up the user's
110124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile entry.
110224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
110324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static class ProfileIdCache {
110424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean inited;
110524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        long profileContactId;
110624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileRawContactIds = Sets.newHashSet();
110724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Set<Long> profileDataIds = Sets.newHashSet();
110824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
110924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        /**
111024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * Initializes the cache of profile contact and raw contact IDs.  Does nothing if
111124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * the cache is already initialized (unless forceRefresh is set to true).
111224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param db The contacts database.
111324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         * @param forceRefresh Whether to force re-initialization of the cache.
111424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro         */
111524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        private void init(SQLiteDatabase db, boolean forceRefresh) {
111624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (!inited || forceRefresh) {
111724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileContactId = 0;
111824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileRawContactIds.clear();
111924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileDataIds.clear();
112024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                Cursor c = db.rawQuery("SELECT " +
112124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_CONTACT_ID + "," +
112224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "," +
112324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        DataColumns.CONCRETE_ID +
112424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " FROM " + Tables.RAW_CONTACTS + " JOIN " + Tables.ACCOUNTS + " ON " +
112524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" +
112624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        AccountsColumns.PROFILE_RAW_CONTACT_ID +
112724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        " JOIN " + Tables.DATA + " ON " +
112824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        RawContactsColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_RAW_CONTACT_ID,
112924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        null);
113024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                try {
113124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    while (c.moveToNext()) {
113224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (profileContactId == 0) {
113324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            profileContactId = c.getLong(0);
113424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
113524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactIds.add(c.getLong(1));
113624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileDataIds.add(c.getLong(2));
113724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
113824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                } finally {
113924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    c.close();
114024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
114124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
114224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
114324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
114424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
114524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private ProfileIdCache mProfileIdCache;
114624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
11473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Limit for the maximum byte size of social stream item photos (loaded from config.xml). */
11483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int mMaxStreamItemPhotoSizeBytes;
11493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
11503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1151b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
115231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
11534097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1154f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1155315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1156622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1157622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
115872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
1159622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1160f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1161a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1162d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1163f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1164a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
116520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
116673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
116720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
116809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
11693826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
117009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
117115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
117215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
117315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1174bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
117573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1176d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private TransactionContext mTransactionContext = new TransactionContext();
1177de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
11781a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
11791a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
118081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
118181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
11824cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
11833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1184d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1185bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1186bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1187bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
11884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
11894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1190de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1191ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1192ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1193ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1194ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1195ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1196ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1197ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
119835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1199ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
120015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
120115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
120215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
12033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Resources resources = getContext().getResources();
12043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mMaxStreamItemPhotoSizeBytes = resources.getInteger(
12053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                R.integer.config_stream_item_photo_max_bytes);
12063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
120724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache = new ProfileIdCache();
1208b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
120972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1210a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
121165ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1212bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
121315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
121415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
121572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1216bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1217bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1218bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1219bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1220bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1221bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1222bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1223bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1224bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
12252a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
122615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1227bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1228bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1229bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1230bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
123105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1232bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
123315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
12343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
123549d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
12364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
12374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1238767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
123951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
124051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
124104b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
124215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
124315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
12444cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
124504b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
12464cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
12474cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
124851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
1249cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
12505b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
125115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
12525b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1253f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
12545b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1255bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
1256bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1257bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE,
12586d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForEmail(context, mDbHelper, mContactAggregator));
1259bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
12606d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForIm(context, mDbHelper, mContactAggregator));
1261bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE,
12626d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForOrganization(context, mDbHelper, mContactAggregator));
1263bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE,
12646d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoneNumber(context, mDbHelper, mContactAggregator));
1265bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
12666d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNickname(context, mDbHelper, mContactAggregator));
1267bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
12686d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredName(context, mDbHelper, mContactAggregator,
1269bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
1270bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
12716d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForStructuredPostal(context, mDbHelper, mContactAggregator,
1272bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
1273bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE,
12746d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForGroupMembership(context, mDbHelper, mContactAggregator,
1275bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
1276bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE,
12776d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForPhoto(context, mDbHelper, mContactAggregator));
12786d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov        mDataRowHandlers.put(Note.CONTENT_ITEM_TYPE,
12796d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                new DataRowHandlerForNote(context, mDbHelper, mContactAggregator));
1280bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1281bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1282bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1283bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1284bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1285bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1286bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1287bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1288bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1289bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1290bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1291bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1292bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1293bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1294bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1295bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1296bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1297bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1298bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
129915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
130015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
130115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
130215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
130315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
130415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
130515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
130615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1307bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
130815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
130915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1310bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1311bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1312bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1313bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1314bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1315bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1316bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1317bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1318bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1319bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1320bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1321bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
132215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
132315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
132415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
132515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
132615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
132715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
132815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
1329bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
1330bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1331bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1332bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1333bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1334bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1335bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1336bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1337bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1338bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1339bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1340fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1341fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1342fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1343fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1344fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1345bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1346bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1347bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1348bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1349bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1350bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1351bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
135205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
135305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
135405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
135505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
135605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1357bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1358bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1359bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1360bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1361bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1362bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1363bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1364bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1365bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1366bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1367bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1368bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
13694cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
13704cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
137153fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
13723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
13733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
13744f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
13754f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
13764f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1377fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
13784cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
137951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
138051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
138151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
138251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
138351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
138451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
138551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
138651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1387bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1388f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1389f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1390f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1391f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1392f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1393f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
139451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
139551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
139651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
139751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
139851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
139951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
140051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
140151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
140251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
1403bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, currentLocale);
1404bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1405bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1406bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
140751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1408fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1409fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1410fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1411fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1412fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1413fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1414fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1415fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
1416fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1417fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
1418fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1419fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1420fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
1421fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1422fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
1423fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1424fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1425fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1426fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1427fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
142805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
142905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
143005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
143105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1432bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1433bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
143451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
143551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
14363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
14373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
14383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
14393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
14403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
14413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
14423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mContactsAccountCount == 0
144349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                && DatabaseUtils.queryNumEntries(mDbHelper.getReadableDatabase(),
144449d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                        Tables.CONTACTS, null) == 0) {
14453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
14463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
14473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
14483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
14493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
14503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
145131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1452de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1453b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1454b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
145531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
145631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1457013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1458013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1459013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1460013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
14615df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
14625df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
14635df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
14645df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
14655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1466ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
146772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
146872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
146972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
147072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
14715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
14725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
14735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
14745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
14753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
1476b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1477b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
14783d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
14793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1480568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1481568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1482568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1483568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1484568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1485bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1486568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1487bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1488bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1489bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1490568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1491bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
1492bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDbHelper.setLocale(this, mCurrentLocale);
1493bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1494568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1495bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1496bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1497bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1498bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1499bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1500bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1501568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1502568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1503bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1504bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1505bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1506bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1507bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1508bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1509bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1510bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1511b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
1512b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1513b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1514bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1515bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1516bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1517bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1518bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1519bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1520bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1521bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1522bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1523bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1524bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1525bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1526bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1527bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1528bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1529bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1530bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1531bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1532bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1533bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1534bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1535bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1536bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1537bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1538bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1539bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1540bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1541bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1542bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
15433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
15443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
15453d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1546568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
15470e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
15483d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
15493d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1550bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1551bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1552bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1553bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1554bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1555bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
15563d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
15573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
15583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1559bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1560bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
15613d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
15623d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1563a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1564a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1565a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1566a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1567b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
15683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1569a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1570a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1571568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
157215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1573568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1574568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1575568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1576568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1577568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
157815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
157915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
158015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
158115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
158215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
158315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
158415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
158515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
158615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
158715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
158815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1589ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1590568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1591568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1592568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1593568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1594568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
159515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1596568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1597568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1598568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1599568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1600568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
160115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
1602bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
1603bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
1604bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
1605bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
1606bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
1607bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
1608bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
1609bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1610bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
1611bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
1612bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
1613bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
1614bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1615bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
161615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1617568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1618568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1619568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1620568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1621568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
162215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1623568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1624568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1625568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1626568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1627568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1628568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
162915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
1630568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1631568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1632568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
16334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
16347b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
16357b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
16367b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        return super.bulkInsert(uri, values);
16377b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
16387b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
16397b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
1640285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1641bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1642b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1643b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1644285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
16451ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1646d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1647b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1648b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1649285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1650285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1651285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
16521129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1653bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1654b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1655b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1656285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1657b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
1658bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
16591a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
16601a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1661b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
16621a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
16633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
1664bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
1665bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
16663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
16673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
16683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
16693826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
1670b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1671b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1672bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
1673bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleContacts = mTransactionContext.getStaleSearchIndexContactIds();
1674bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        Set<Long> staleRawContacts = mTransactionContext.getStaleSearchIndexRawContactIds();
1675bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
1676bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
1677bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mTransactionContext.clearSearchIndexUpdates();
1678bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
1679bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
1680bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
1681b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1682bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1683b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1684b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
16851129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
168624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Determine whether we need to refresh the profile ID cache.
168724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        boolean profileCacheRefreshNeeded = false;
168824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1689d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (long rawContactId : mTransactionContext.getInsertedRawContactIds()) {
16908ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov            mDbHelper.updateRawContactDisplayName(mDb, rawContactId);
1691bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.onRawContactInsert(mTransactionContext, mDb, rawContactId);
1692285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1693b5a4add17815167d20a90645779df34cdf45280dFred Quintana
169424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        Map<Long, Account> insertedProfileRawContactAccountMap =
169524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mTransactionContext.getInsertedProfileRawContactIds();
169624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (!insertedProfileRawContactAccountMap.isEmpty()) {
169724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            for (long profileRawContactId : insertedProfileRawContactAccountMap.keySet()) {
169824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mDbHelper.updateRawContactDisplayName(mDb, profileRawContactId);
169924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mContactAggregator.onProfileRawContactInsert(mTransactionContext, mDb,
170024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        profileRawContactId,
170124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        insertedProfileRawContactAccountMap.get(profileRawContactId));
170224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
170324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = true;
170424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
170524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1706d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> dirtyRawContacts = mTransactionContext.getDirtyRawContactIds();
1707d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
1708a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1709a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1710d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
1711a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1712a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
171324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
171424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
171524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, dirtyRawContacts);
1716a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1717a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1718d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> updatedRawContacts = mTransactionContext.getUpdatedRawContactIds();
1719d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
1720a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1721a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1722d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
1723a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1724a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
172524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
172624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            profileCacheRefreshNeeded = profileCacheRefreshNeeded ||
172724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    !Collections.disjoint(mProfileIdCache.profileRawContactIds, updatedRawContacts);
1728b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1729b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1730d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (Map.Entry<Long, Object> entry : mTransactionContext.getUpdatedSyncStates()) {
1731b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
17329d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
17339d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
17349d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
17359d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
1736b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1737b5a4add17815167d20a90645779df34cdf45280dFred Quintana
173824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (profileCacheRefreshNeeded) {
173924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Force the profile ID cache to refresh.
174024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mProfileIdCache.init(mDb, true);
174124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
174224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1743d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
1744b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1745b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1746a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1747a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1748a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1749a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1750d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
1751b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1752a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1753b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1754a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1755a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1756285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1757285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
175824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
175924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given contact ID represents the user's personal profile - if it is, calls
176024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * a permission check (for writing the profile if forWrite is true, for reading the profile
176124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * otherwise).  If the contact ID is not the user's profile, no check is executed.
176224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param contactId The contact ID to be checked.
176324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
176424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
176524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermissionForContact(long contactId, boolean forWrite) {
176624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache.init(mDb, false);
176724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileContactId == contactId) {
176824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
176924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
177024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
177124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
177224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
177324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given raw contact ID is a member of the user's personal profile - if it
177424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * is, calls a permission check (for writing the profile if forWrite is true, for reading the
177524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the raw contact ID is not in the user's profile, no check is
177624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * executed.
177724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param rawContactId The raw contact ID to be checked.
177824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
177924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
178024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermissionForRawContact(long rawContactId, boolean forWrite) {
178124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache.init(mDb, false);
178224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileRawContactIds.contains(rawContactId)) {
178324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
178424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
178524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
178624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
178724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
178824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Checks whether the given data ID is a member of the user's personal profile - if it is,
178924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * calls a permission check (for writing the profile if forWrite is true, for reading the
179024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * profile otherwise).  If the data ID is not in the user's profile, no check is executed.
179124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param dataId The data ID to be checked.
179224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
179324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
179424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermissionForData(long dataId, boolean forWrite) {
179524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        mProfileIdCache.init(mDb, false);
179624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (mProfileIdCache.profileDataIds.contains(dataId)) {
179724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(forWrite);
179824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
179924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
180024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
180124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
180224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Performs a permission check for WRITE_PROFILE or READ_PROFILE (depending on the parameter).
180324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * If the permission check fails, this will throw a SecurityException.
180424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forWrite Whether the caller is attempting to do a write (vs. read) operation.
180524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     */
180624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void enforceProfilePermission(boolean forWrite) {
180724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String profilePermission = forWrite
180824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? "android.permission.WRITE_PROFILE"
180924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : "android.permission.READ_PROFILE";
181024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        getContext().enforceCallingOrSelfPermission(profilePermission, null);
181124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
181224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1813285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1814cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
181581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
181681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
181781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
181881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
181981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
182081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
182181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
1822cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
1823568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
182451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
18253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
18263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
18273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
18283826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
182951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
183051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1831f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
18323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
18333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
18346d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
18356d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov                    getContext(), mDbHelper, mContactAggregator, mimeType);
18363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
18373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
18383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
18393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
18403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
18414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1842de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
1843bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
18441129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
1845b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1846f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1847f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
1848f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
1849f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1850a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
1851a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
185235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1853a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
185435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
1855b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
185635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
185735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1858d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
1859d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
18606bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
18616bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
18626bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
186324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
186424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
186524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
186624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
186724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
18685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
186924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, false);
1870f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1871a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1872a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1873a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
18745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
18755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
1876f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1877f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1878a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1879a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1880a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
18813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
18823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
18833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
18843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
18853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
18863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
18873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
188824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
188924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(true);
189024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter, true);
189124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
189224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
189324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
189424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
1895a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
1896f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1897f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1898a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1899a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1900a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1901ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1902f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
1903f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1904ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1905ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1906ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1907eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
19085aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
190943880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
1910eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
1911eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
1912eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
191382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
191482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
19151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
19161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
19171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
19183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
19193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
19203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
19213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
19223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
19233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
19243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
19253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
19263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
19273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
19283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
19293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
19303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
19313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
19323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
19333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
19343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
19353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
19363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1937a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
193881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
1939f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
1940a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1941a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
19427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
19437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
19447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
19457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1946de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
1947a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1948a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1949a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1950e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
1951e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
1952e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
1953e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
1954e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
1955e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
1956e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
1957e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
1958e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
1959e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
1960e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
1961e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
1962e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
19637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
1964e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
1965f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
1966f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
1967e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
1968f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
1969f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
1970f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
1971e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
1972e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
1973e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1974e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
1975e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
1976fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
1977fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
1978e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
1979e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1980e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
1981e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
1982e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
1983e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
1984e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
1985e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
1986e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
1987e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
1988e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
1989e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
1990fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
1991fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
1992e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
1993e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
1994e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
1995f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
1996f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
1997e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
1998f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
1999f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2000e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2001e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2002f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2003f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2004e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2005f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2006f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2007f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2008f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2009035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2010f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2011e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
20127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
20137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
20147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2015d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
20166bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
20176bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
20186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
20196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2020d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2021de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
20226bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
20236bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
20246bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
202524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2026a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2027f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2028f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2029dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
203024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * @param forProfile Whether this raw contact is being inserted into the user's profile.
2031a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2032a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
203324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter,
203424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            boolean forProfile) {
2035f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2036f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2037f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2038f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2039e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
20407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
20413d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
20423d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2043f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
20443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
20453d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2046f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2047f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
204824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
204924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Profile raw contacts should never be aggregated by the aggregator; they are always
205024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // aggregated under a single profile contact.
205124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            aggregationMode = RawContacts.AGGREGATION_MODE_DISABLED;
205224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2053f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2054f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2055f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2056285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
205724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (forProfile) {
205824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of the user profile Contact (or association with the existing one)
205924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // at the end of the transaction.
206024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mTransactionContext.profileRawContactInserted(rawContactId, account);
206124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        } else {
206224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Trigger creation of a Contact based on this RawContact at the end of transaction
206324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            mTransactionContext.rawContactInserted(rawContactId, account);
206424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
2065f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2066dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2067dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2068dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2069dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2070dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2071dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2072dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2073dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
20743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2075023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2076a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2077a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2078dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2079dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2080dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2081dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2082dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2083dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2084dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2085dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2086dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
2087dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
2088dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
2089dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2090dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2091dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2092dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2093dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2094dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2095dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2096dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2097dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2098dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2099dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2100dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2101dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2102dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2103dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2104dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2105dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2106dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2107dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2108dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2109dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2110dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2111dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2112dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2113dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2114dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2115dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2116dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2117dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2118dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2119dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2120dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2121dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2122dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2123dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2124dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2125dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2126dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2127dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2128dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2129dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2130a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2131a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2132a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2133a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2134a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2135a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2136f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2137a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2138de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2139de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
214067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2141de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
214220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
214324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // If the data being inserted belongs to the user's profile entry, check for the
214424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // WRITE_PROFILE permission before proceeding.
214524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForRawContact(rawContactId, true);
214624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2147de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2148de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2149de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2150b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2151de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2152de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2153508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2154de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2155de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2156de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2157de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2158de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
21594097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2160b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2161de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2162a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2163a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2164d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        id = rowHandler.insert(mDb, mTransactionContext, rawContactId, mValues);
2165f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2166d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.markRawContactDirty(rawContactId);
2167a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2168d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactUpdated(rawContactId);
2169a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
21704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
21714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
21723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
21733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
21743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
21753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
21763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
21773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
21783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
21793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
21803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
21813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
21823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
21833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
21843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
21853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
21863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
21873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
21883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
21893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
21903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If the data being inserted belongs to the user's profile entry, check for the
21913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // WRITE_PROFILE permission before proceeding.
21923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceProfilePermissionForRawContact(rawContactId, true);
21933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
21943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Ensure that the raw contact exists and belongs to the caller's account.
21953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, mValues);
21963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccount(account, rawContactId);
21973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
21983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
21993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = mDb.insert(Tables.STREAM_ITEMS, null, values);
22003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
22023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
22033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
22043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
22053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
22073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
22083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
22103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
22113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
22123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
22133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
22143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
22153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item photo _ID of the newly created row
22163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
22173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
22183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
22193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
22203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
22213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
22233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
22243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
22253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // If the data being inserted belongs to the user's profile entry, check for the
22273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // WRITE_PROFILE permission before proceeding.
22283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceProfilePermissionForRawContact(rawContactId, true);
22293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Ensure that the raw contact exists and belongs to the caller's account.
22313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Account account = resolveAccount(uri, mValues);
22323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceModifyingAccount(account, rawContactId);
22333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Make certain that the photo doesn't exceed our maximum byte size.
22353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PICTURE);
22363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Log.i(TAG, "Inserting " + photoBytes.length + "-byte photo (max allowed " +
22373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    mMaxStreamItemPhotoSizeBytes + " bytes)");
22383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (photoBytes.length > mMaxStreamItemPhotoSizeBytes) {
22393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new IllegalArgumentException("Stream item photos cannot be more than " +
22403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    mMaxStreamItemPhotoSizeBytes + " bytes (received picture with " +
22413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        photoBytes.length + " bytes)");
22423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
22433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            id = mDb.insert(Tables.STREAM_ITEM_PHOTOS, null, values);
22453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
22463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
22473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
22483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
22503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
22513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
22523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
22533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
22543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
22553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
22563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = mDb.query(Tables.STREAM_ITEMS, new String[]{StreamItems.RAW_CONTACT_ID},
22573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
22583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
22593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
22603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
22613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
22623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
22633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
22643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
22653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
22663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
22673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
22683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
22693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
22703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given raw contact ID is owned by the given account.
22713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account is null, this will return true iff the raw contact
22723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * is also associated with the "null" account.
22733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
22743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account does not match, this will throw a security exception.
22753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
22763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to check for.
22773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
22783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void enforceModifyingAccount(Account account, long rawContactId) {
22793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
22803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
22813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
22823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
22833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
22843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
22853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c;
22863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (account != null) {
22873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContactsColumns.CONCRETE_ID},
22883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    accountSelection,
22893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
22903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
22913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } else {
22923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContactsColumns.CONCRETE_ID},
22933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    noAccountSelection, new String[]{String.valueOf(rawContactId)},
22943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
22953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
22963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
22973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if(c.getCount() == 0) {
22983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new SecurityException("Caller account does not match raw contact ID "
22993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    + rawContactId);
23003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
23013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
23023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
23033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
23043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
23053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
23063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
23073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream items matches up with the given
23083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
23093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
23103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
23113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
23123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
23133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item IDs that would be included in this selection.
23143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
23153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
23163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
23173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = Lists.newArrayList();
23183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
23193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItems(qb);
23203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = qb.query(mDb,
23213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
23223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
23233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
23243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
23253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemIds.add(c.getLong(0));
23263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
23273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
23283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
23293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
23303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
23313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
23323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
23333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds;
23343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
23353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
23363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
23373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream item photos matches up with the given
23383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
23393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
23403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
23413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
23423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
23433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item photo IDs that would be included in this selection.
23443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
23453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
23463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
23473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemPhotoIds = Lists.newArrayList();
23483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
23493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItemPhotos(qb);
23503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = qb.query(mDb, new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
23513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
23523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
23533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
23543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemPhotoIds.add(c.getLong(0));
23553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
23563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
23573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
23583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
23593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
23603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
23613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
23623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemPhotoIds;
23633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
23643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
23653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
23663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
23673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
23683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
23693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
23703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
23713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
23723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
23733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
23743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
23753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
23763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
23773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
23783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c = mDb.query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
23793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
23803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
23813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
23823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
23833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
23843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
23853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
23863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
23873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
23883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
23893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
23903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
23913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
23923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
23933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
23943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
23953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
23963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
23973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
23983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
23993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
24003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
24013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
24023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
24033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2404ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
24058ab0b7a48efe540226253567bcf6fdbc487186a2Dmitri Plotnikov        mDbHelper.updateRawContactDisplayName(db, rawContactId);
2406d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2407d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
24089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
240920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
241020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2411f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
241220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
241320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2414de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2415de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
2416f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS,
2417f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2418de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2419de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
2420f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
242124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
242224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for write profile permission if the data belongs to the profile.
242324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, true);
242424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2425f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
2426a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2427d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                count += rowHandler.delete(mDb, mTransactionContext, c);
2428f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
2429d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                    mTransactionContext.markRawContactDirty(rawContactId);
243088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
243120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
243220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2433de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
243420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
243520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
243620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
243720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
243820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
243988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
244088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
244188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
244220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2443f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
244488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
244588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
24464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
2447f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
24484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2449f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
245020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
245120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
245220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
245320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
245420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2455f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
245620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
245720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
245820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
245920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
246020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
246120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
246220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
246320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
246420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
24657a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
246620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
246720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
246820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
246924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // Check for write profile permission if the data belongs to the profile.
247024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
247124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermissionForRawContact(rawContactId, true);
247224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2473a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2474d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return rowHandler.delete(mDb, mTransactionContext, c);
247520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
247620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
247720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
247820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
247920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
248020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2481ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2482ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2483f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2484f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2485f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2486f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2487e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
2488ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2489ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2490f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
249167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2492f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
249367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2494f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2495ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2496dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
2497dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
2498dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
2499dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2500f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2501f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
250273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
250373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2504f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2505ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2506dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
2507dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
2508dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
2509dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
2510dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
2511dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
2512dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
2513dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
2514dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2515dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
2516dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
2517dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
2518dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2519dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
2520dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
2521dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
2522892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
2523892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
2524892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
2525892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
2526892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
2527d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mTransactionContext.markRawContactDirty(rawContactId);
2528892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
2529dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2530892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
2531892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
2532dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2533dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2534dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2535f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
25361a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2537ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2538ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2539ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2540ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2541ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
25425aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2543e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
25445aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
25451a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
25461a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2547e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
25481a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2549e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2550e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2551e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2552ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
255382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
25541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
255582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
255682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
25570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
25584dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
25594dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
25600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
256182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
25624dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
25634dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
25644dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
25654dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
25661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
25671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2568dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2569dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
257082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2571f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
25722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
2573dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2574dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2575dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
25762526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
25772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
25781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2579dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2580dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
25810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
25820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
25830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
25840a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2585dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2586dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2587dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
25882a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mDbHelper.getMimeTypeIdForIm());
2589dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
25902a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mDbHelper.getMimeTypeIdForEmail());
2591f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2592f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2593f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2594f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2595f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2596f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2597f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
25982526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
25992526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
26002526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
26012526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
26022526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
26032526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
26042526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
26052526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
2606dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
26072526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
26082526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2609dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
26102526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
26112526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
2612dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
26132526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
26142526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
26152526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
26162526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
26172526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
26182526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
2619dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
26202526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
26212526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
2622dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2623dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
26241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
262582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
26262526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
26272526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
2628dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
262970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
263070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
26311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
26321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2633de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
26342526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
26354394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
26361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
263767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
26385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2639e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
26401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
26411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
26421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
26431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
26441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
264531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
264631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
264731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
26481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
26491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
265082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2651a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2652a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2653a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2654a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2655a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2656a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2657a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
265882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2659a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2660a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
266182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
266282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
266382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
266482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
266582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2666a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
266782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
266882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
2669aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
2670aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
26711f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2672a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2673a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2674a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2675e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
26760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
267782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
267882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
26790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
26800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
26810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
26830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
26840a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
26850a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
26860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
26870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26880a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
26890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
26900a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2691a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
269278fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteStatusUpdate(dataId);
269382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
269482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
269578fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.replaceStatusUpdate(dataId, timestamp, status, resPackage, iconResource,
269678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
2697a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
269878fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.insertStatusUpdate(dataId, status, resPackage, iconResource,
269978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
2700e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2701e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2702bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2703a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2704f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
2705a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2706a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2707a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
27081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
27091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
27104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2711de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2712bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2713b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2714b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2715b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2716f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2717f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2718508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2719508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
272035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2721b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
272235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2723b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2724b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2725b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2726b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2727b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2728b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2729cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2730cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2731cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2732cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2733cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2734d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2735d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2736dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
27376bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
27386bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
27399fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
27402e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
27412e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
27422e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
2743fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2744fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
27452e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
27462e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
27472e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
2748dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
27492e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
27502e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
27519fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
27529fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
27539fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
27549fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
27559fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
27569fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
2757a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
27589fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
27599fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
27609fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
27619fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
27629fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
27639fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
27649fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
27659fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
276660de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
27679fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
27689fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
27699fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
27709fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
27719fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
27729fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
2773dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
27749fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
27759fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
27769fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
27779fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
27789fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
27799fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
27809fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
27819fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
27829fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
27839fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
27842971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
27852971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
2786fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
2787fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
2788e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
27892971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
27902971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
27912971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2792fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
2793fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
2794fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
27952971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
27962971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
27972971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
27982971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
27992971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
28002971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
28012971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
28025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
28032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2804fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
2805fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
2806508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2807508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
280820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2809f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2810944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2811f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
281220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
281320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
281448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
281548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
281648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
281748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2818508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2819f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
28204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
28214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
2822ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2823ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2824ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2825f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
28265aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
28272971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
28282971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
28292971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
28302971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
28312971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2832e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
28332971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
28342971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
28355aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
28362971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
28372971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
28382971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
28392971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
284081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2841f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
284281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
28432971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2844508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2845508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2846eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
284743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2848e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
2849eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2850eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
285182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
28520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
28531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
28541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
28553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
28563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
28573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
28583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
28613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
28623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
28633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemsColumns.CONCRETE_ID + "=?",
28643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
28653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
28683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
28693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(), selection, selectionArgs);
28703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
28733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
28743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
28753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
28763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
28773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
28783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
28793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
28803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
288281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
288381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
28843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
288581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2886508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
28874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
28884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
28891c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
2890ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2891b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
289294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2893de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
289494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
289594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
289694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
289794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2898f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2899de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
290094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
290194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
290294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2903f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2904de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
290594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
290694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
29071a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
290894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
290994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
291094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
29115aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2912e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
29131a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
2914e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2915e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2916e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2917dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
291824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForContact(contactId, true);
291996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
2920cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
292196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
292296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
2923cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2924cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2925cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2926dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
2927cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2928cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2929cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2930cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2931cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
29323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
29333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2934cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2935cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2936cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2937fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
293824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForRawContact(rawContactId, true);
29393389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
29403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
29413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2942f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
294314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2944fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
2945fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
2946fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
294733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2948b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2949dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
295033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
295133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
295233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
29530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
29549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
29559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
29569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
29579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
29589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
29599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
29609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
29619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
29620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
29630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
29643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
29653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
29663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream items to be deleted, and check that they belong
29673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // to the account.
29683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
29693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
29703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                account, selection, selectionArgs);
29713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
29733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        for (long streamItemId : streamItemIds) {
29743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            deleteStreamItem(streamItemId);
29753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mVisibleTouched = true;
29783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds.size();
29793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
29823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
29833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
29843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
29853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
29863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
29893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
29903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream item photos to be deleted, and check that they
29913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // belong to the account.
29923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
29933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
29943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
29963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
29973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
30003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
30013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.delete(Tables.STREAM_ITEM_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID + "=?",
30023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
30033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
30043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3005dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
300681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
300781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3008cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3009cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3010cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3011cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3012cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3013cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3014dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3015cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3016cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
30174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3018de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3019de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3020bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3021b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3022b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3023b5a4add17815167d20a90645779df34cdf45280dFred Quintana
302435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
302500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
302600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3027b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3028b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
30291129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3030d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.syncStateUpdated(rowId, data);
3031b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3032b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3033b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3034f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3035f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
303600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
303735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3038b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3039b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3040b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3041b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3042b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3043b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3044b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3045b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3046b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3047b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3048b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
304935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3050d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3051dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
305200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
305300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
305400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3055d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3056dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3057c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3058c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3059c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
306024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
306124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Restrict update to the user's profile.
306224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder profileSelection = new StringBuilder();
306324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                profileSelection.append(Contacts.IS_USER_PROFILE + "=1");
306424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (!TextUtils.isEmpty(selection)) {
306524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    profileSelection.append(" AND (").append(selection).append(")");
306624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
306724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                count = updateContactOptions(values, profileSelection.toString(), selectionArgs,
306824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        callerIsSyncAdapter);
306924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
307024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
307124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
30722e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
30732e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
30742e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
30752e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
30762e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3077fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3078fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
30792e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
30802e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
30812e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3082dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
30832e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
30842e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
30852e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
30867d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
30877d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
30887d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
30897d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
30907d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
30917d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
30927d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
30937d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
30947d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
30957d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
309620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3097944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3098f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
309981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3100f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
310181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
310220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
310320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3104c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
310548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
310648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
310748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
310848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3109f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
311081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3111f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
311281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
311300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
311400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
31157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
31165ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
31175ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3118dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
31197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
31207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
31217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
31225ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
312333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
31244529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
31254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
31264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3127dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3128dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
31294529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
31304da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3131dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3132dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
31334529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
31347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
31357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
31367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3137ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
31385aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3139f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
314081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3141f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
314281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3143ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3144ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3145ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3146ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3147ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
31484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
31494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
315073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
31515aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
31525aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
315381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3154f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
315581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3156ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3157ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3158ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3159127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3160de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3161b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3162b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3163b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3164eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3165e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3166e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
316743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3168eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3169eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3170eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
31719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
31729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
31739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
31749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
31759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
31763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
31773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
31783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
31793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
31813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
31823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, StreamItemsColumns.CONCRETE_ID + "=?",
31833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
31843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
31853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
31873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
31883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
31893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
31903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
31923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
31933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
31943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
31953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
31963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
31973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
31983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
31993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
32003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
32013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
32023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
32033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
32043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
32053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
32063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
32073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
32083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
320972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
3210bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
321172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
3212d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3213d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3214d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
321546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
321646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
321746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
321846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
321946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
322046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
322146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
322246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
322346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
322481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
322581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3226f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
322781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
322800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
322900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
323000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
32314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
32324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
32339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
32349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
32359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
32369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
32379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
32389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
32399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
32409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
32419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
32429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
32439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
32449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
32459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
32469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
32479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
32489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
32499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
32509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
32519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
32529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
32539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
32549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
32559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
32569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
32573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
32583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
32593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
32603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
32613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream items being updated belong to the account.
32633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
32643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
32653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
32673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
32683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
32693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
32713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
32723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
32733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
32743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream item photos being updated belong to the account.
32763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
32773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
32783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
32803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return mDb.update(Tables.STREAM_ITEM_PHOTOS, values, selection, selectionArgs);
32813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
32823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
32839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
32849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
32859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
32869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
32879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
32889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
32899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
32909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
32919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
32929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
32939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
32949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
32959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
32969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
32979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
32989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
32999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
33009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
33019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
33029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
33039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
33049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
33059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
33069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
33079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
33089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
33099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
33109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
33119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
33129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3313aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3314aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
33159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
33169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
33179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
33185aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3319f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
332073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3321ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3322ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
332373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3324f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
332573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
332673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
332773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
332873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
332973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
333073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
333173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
333273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3333ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
33341a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
33351a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
333694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
33376ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
33381129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
33396ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3340e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
33416ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
33426ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
33436ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
33446ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
33456ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
33466ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
33476ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
334824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
33496ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3350ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
33516ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
33526ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
33536ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
33546ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
33556ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
33566ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
33576ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
33586ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
335994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
336094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
336194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3362b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3363b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3364e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
33651a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
33661a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3367e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3368e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3369e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3370e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3371dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
3372dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
33734529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
33744529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
33754529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
33764529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
337773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
337897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
337997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
338097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
338197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
338297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
33834529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3384ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        Cursor cursor = mDb.query(Views.RAW_CONTACTS,
338551bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
33864529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
33874529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
33884529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
33894529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
3390dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
33914529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
33924529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
33934529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
33944529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
33954529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
33964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
33974529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
33984529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
33994529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
3400dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
3401dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
340224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
340324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Enforce profile permissions if the raw contact is in the user's profile.
340424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForRawContact(rawContactId, true);
340524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
340696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
340796b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
340819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
340919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
341019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3411ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3412ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
341319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
341419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
341596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
341619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
341719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
341819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3419ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3420ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
342119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
342219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
342319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
342419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
342519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
342619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
342719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3428f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
342996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
34305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3431f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3432f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3433f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3434f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3435f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3436f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
343769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
3438f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3439f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3440433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
3441dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
3442dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3443dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
3444dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
34454529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3446dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3447dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
3448dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
3449dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
3450dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
3451dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3452dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
3453dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
3454dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
3455dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
3456dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3457dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3458dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3459dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
3460dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3461dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3462dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3463433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3464dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3465285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
34662b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3467285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3468f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3469f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3470f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3471f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3472f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
347378fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.resetNameVerifiedForOtherRawContacts(rawContactId);
3474f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3475f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3476f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
347719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
3478d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mTransactionContext.rawContactInserted(rawContactId,
3479d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        new Account(accountName, accountType));
348019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
34815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
34825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
348333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
348433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3485321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3486f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
348720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
348820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
348920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
34905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
349120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
349220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
349320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
349420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
349520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3496b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
349720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
349820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
349997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
350097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
350197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
350297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
350397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
3504653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
350520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3506653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3507653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
3508f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(uri, DataRowHandler.DataUpdateQuery.COLUMNS,
3509f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
3510653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3511653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
351224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check profile permission for the raw contact that owns each data record.
351324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = c.getLong(DataRowHandler.DataUpdateQuery.RAW_CONTACT_ID);
351424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, true);
351524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3516f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
351720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3518653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3519653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
352020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
352120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3522653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
352320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
352420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3525f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3526653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3527653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3528321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3529653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
3530f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
3531a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3532d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (rowHandler.update(mDb, mTransactionContext, values, c, callerIsSyncAdapter)) {
3533813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
3534813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
3535813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
3536a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
3537321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3538321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
35398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
3540dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
35418c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3542ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        Cursor cursor = mDb.query(Views.CONTACTS,
354324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                new String[] { Contacts._ID, Contacts.IS_USER_PROFILE }, selection,
35448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
35458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
35468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
35478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
354824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
354924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                // Check for profile write permission before updating a user's profile contact.
355024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean isProfile = cursor.getInt(1) == 1;
355124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                if (isProfile) {
355224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermission(true);
355324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
355424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
3555dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
35568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
35578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
35588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
35598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
35608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
35618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
35628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
35638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
35648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3565dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
3566dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
3567d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
356824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // Check write permission if the contact is the user's profile.
356924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        enforceProfilePermissionForContact(contactId, true);
357024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
35718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3572b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3573d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3574b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3575d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3576b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3577d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3578b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3579d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3580b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3581d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3582d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3583d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
35848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3585d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3586d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3587d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
35888c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3589c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
35908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3591c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3592c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
35934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
359497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
359597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
35968c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3597dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
3598ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann            Cursor cursor = mDb.query(Views.RAW_CONTACTS,
3599dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
3600dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
3601dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
3602dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
3603dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
3604dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3605dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
3606dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3607dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
3608dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
3609dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3610dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3611dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
36128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
36138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
36148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3615b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
36168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3617b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
36188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3619b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
36208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3621b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
36228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3623b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
36248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
36258c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
36269b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
36276e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
36289b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
36299b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
36309b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
36319b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
36329b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
36339b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
3634f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3635d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3636127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3637127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
36380c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
36390c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
364080c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
3641ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
3642ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
36430c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
36440c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
36450c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
36460c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
36470c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
36480c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3649b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3650127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
36510c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
36524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
36534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
36540c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
36554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
36564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
36570c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
36586bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
36596bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
36600c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
36610c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
36620c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
36630c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3664127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3665127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
36663389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
366769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
366869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
366969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
367069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
3671dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
3672bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId1);
3673bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        mContactAggregator.aggregateContact(mTransactionContext, db, rawContactId2);
3674127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3675127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3676127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3677127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3678b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3679b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
368070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
3681bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
36823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
36833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3684bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
3685f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
3686e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
3687627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
368849d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
368970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
369070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
3691dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
3692743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
3693743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
3694743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
3695743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
3696e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
3697743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
3698743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
3699743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
3700743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
3701743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
370248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3703627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
3704743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
3705627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
3706627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
3707627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
370870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
370970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
371033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
3711e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
3712e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
3713e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
3714e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
3715e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3716e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
3717e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
3718e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
3719e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3720e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
3721e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
3722e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
3723e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
3724e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3725e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
3726e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3727e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
3728e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3729e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
3730e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3731e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
3732e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
3733e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
3734e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3735e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
3736e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
3737e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
3738d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
3739d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
3740d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
3741d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
37424458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
3743e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
3744e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
374533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
374633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
3747e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
374833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
374933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
375033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
375133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
375269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
375369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
375469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
375533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
375633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
375769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
375869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
375933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
376033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
376133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
376233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
376333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
376433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
376533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
376633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
376733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
3768bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                    mContactAggregator.updateAggregateData(mTransactionContext, contactId);
376933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
3770e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
3771bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                updateSearchIndexInTransaction();
377233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
377333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
3774e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
3775e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
3776e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
377770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
377870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
377970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
378070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
378173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
37823826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
37833826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
37843826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
37853826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
37863826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
37873826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3788afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
378970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3790619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
37913826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
37923826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
37933826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
37943826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
37953826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
37963826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
37973826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
37983826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
37993826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
38003826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
38013826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
38023826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
38033826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
38043826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
38053826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
38063826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
38073826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
38083826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
38093826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
38103826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
381172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
3812bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
3813d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3814d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3815619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3816627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
3817627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
3818dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
3819743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
3820743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
3821743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
3822627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
3823627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
3824dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
3825627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
3826627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3827627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3828627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
3829627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
3830627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
3831627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
3832627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
38334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
38344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
38354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
383615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
383715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
383815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
3839d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
3840385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
38413716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
38423716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1));
3843385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
38443716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
38453716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
38463716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                            Directory.DEFAULT));
3847d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
38483716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            return wrapCursor(uri,
38493716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
38503716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                            Directory.LOCAL_INVISIBLE));
3851d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3852d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3853d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
3854d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
3855a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
3856a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
3857d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3858d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3859d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
3860d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
3861d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
3862d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
3863d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
3864d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
3865d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
3866d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
3867d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
3868d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
38692e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
38702e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
38712e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
38722e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
38732e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
38742e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
3875d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
387609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
387709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
387809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
387909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
388009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
3881332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
3882d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
38836ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
38846ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
38856ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
38866ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
38876ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
3888547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
3889547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
3890547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return wrapCursor(uri, cursor);
3891547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
3892547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return matrixCursorFromCursor(wrapCursor(uri, cursor));
3893547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
38943716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
38953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
3896547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro    private Cursor wrapCursor(Uri uri, Cursor cursor) {
3897547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
3898547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
3899547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
3900547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro            return cursor;
3901547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
3902547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
39033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
39043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
39053716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
39063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
39073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
39083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
39093716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
39103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
39113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
39123716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
39133716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
39143716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
39153716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
39163716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
39173716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
39183716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
39193716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
39203716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
3921547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        return new SnippetizingCursorWrapper(cursor, query, startMatch, endMatch, ellipsis,
3922547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro                maxTokens);
39236ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
39246ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
39256ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
39266ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
39276ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
39286ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
39296ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
39306ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
39316ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
39326ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
39336ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
39346ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
39356ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
39366ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
39376ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
39386ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
39396ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
39406ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
39416ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
39426ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
39436ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
39446ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
39456ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
3946332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
39476ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
3948d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3949d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3950d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
3951d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
3952d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
3953d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
3954d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
3955d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
3956d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
3957d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3958d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
3959d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
3960d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
3961d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
3962d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3963d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3964d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
3965d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
3966d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
3967d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
39684458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
39694458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
39704458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
397149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
397249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
39734458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
39744458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
39754458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
39764458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
39774458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
39784458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
39794458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
39804458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
39814458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
39824458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
39834458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
39844458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
39854458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
3986d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
39874458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
3988d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3989d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
39904458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
39914458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
3992d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
3993d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
399472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
39954458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
39964458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
39974458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
399872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
399972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
4000d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    public Cursor queryLocal(Uri uri, String[] projection, String selection, String[] selectionArgs,
4001385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                String sortOrder, long directoryId) {
4002bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4003bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4004bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
40050b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4006b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
400735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4008d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
40091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4010c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4011c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4012a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
40134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
401435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4015b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
401635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
401735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4018d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4019763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
402024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                boolean existingWhere = appendLocalDirectorySelectionIfNeeded(qb, directoryId);
402124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, Contacts.IS_USER_PROFILE, existingWhere);
402224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder);
4023619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4024619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4025619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4026d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
40274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
402824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForContact(contactId, false);
4029763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
40304da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
40314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
40326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
40336bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
40346bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
40355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
40365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
40375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
40385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
40395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4040fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4041fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
40425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4043a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
40445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
40455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
40465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
404724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermissionForContact(contactId, false);
40485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4049763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4050a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4051a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4052a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4053a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4054a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
40555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
40565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
40575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
40585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4059763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
40604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
40614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
40624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
40635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
40645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
40655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40662149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
40672149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
40682149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
40692149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
40702149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
40712149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
40722149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
40732149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
40742149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
40752149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
40762149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
407724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermissionForContact(contactId, false);
40782149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
40792149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
4080a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4081a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4082a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4083a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
4084a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
40852149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
40862149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
40872149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
40882149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
40892149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
40902149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
40912149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
409224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
409324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForContact(contactId, false);
40942149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
409524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
40962149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
40972149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
40982149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
40992149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
41003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
41013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
41023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceProfilePermissionForContact(contactId, false);
41033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
41043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
41053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContactsColumns.CONCRETE_CONTACT_ID + "=?");
41063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
41073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
41083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
41103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
41113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
41123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
41133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
41143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
41153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
41163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
41173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
41183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
41193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
41203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    enforceProfilePermissionForContact(contactId, false);
41213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
41223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
41233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
41243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
41253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            RawContacts.CONTACT_ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
41263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
41273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
41283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
41293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
41303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
41323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = lookupContactIdByLookupKey(db, lookupKey);
41333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceProfilePermissionForContact(contactId, false);
41343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
41353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
41363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
41373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
41383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
4139f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
414042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
414124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long contactId = lookupContactIdByLookupKey(db, lookupKey);
414224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForContact(contactId, false);
4143ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
4144f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
41454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
414624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
41474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4148f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4149f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4150f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
415142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
415242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
415342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
415442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
415542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
415642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
415742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
415842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
415942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
416042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4161ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4162916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4163ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4164916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4165ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
41667ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
41677ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                        qb, uri, projection, filterParam, directoryId);
416824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, Contacts.IS_USER_PROFILE, false);
416924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = prependProfileSortIfNeeded(uri, sortOrder);
4170ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4171ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4172ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4173ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4174ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
41752f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
41762f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
41772f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
41782f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
41792f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
41802f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
41812f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
41822f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
41832f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
41844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
41854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4186e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
41875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
41882f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
41894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
41904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
41912f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
41925e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
41932f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
41945e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
41955e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
41964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
41972f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection,
41982f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        false /* for frequent */, phoneOnly);
41992f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setProjectionMap(sStrequentStarredProjectionMap);
42002f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.appendWhere(DbQueryUtils.concatenateClauses(
42012f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        selection, Contacts.IS_USER_PROFILE + "=0"));
42022f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
42032f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String starredQuery = qb.buildQuery(subProjection,
420424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
4205d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
42062f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
4207d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
42082f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
42092f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Build the second query for frequent
42102f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection,
42112f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        true /* for frequent */, phoneOnly);
421224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sStrequentFrequentProjectionMap);
42132f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.appendWhere(DbQueryUtils.concatenateClauses(
42142f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        selection, Contacts.IS_USER_PROFILE + "=0"));
42152f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
42162f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String frequentQuery = qb.buildQuery(subProjection,
42172f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)",
421824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts._ID, null, null, null);
4219d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4220d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
42212f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
42222f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
42232f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
42242f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
42252f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
42262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
42272f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
42282f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
42292f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
42302f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
42312f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
42322f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
42332f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
42342f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
42352f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
42362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
42377d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
42387d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
42392f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
42402f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
42412f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                Cursor cursor = db.rawQuery(unionQuery, doubledSelectionArgs);
42422f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
42432f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
4244d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4245d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
42462f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
4247d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4248d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4249ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4250763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4251b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
425271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
42534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4254b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4255b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4256b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4257b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
425824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
425924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
426024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
426124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
426224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
426324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
426424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
426524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
426624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
426724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
426824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Contacts.IS_USER_PROFILE + "=1");
426924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
427024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
427124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
427224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA: {
427324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
427424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
427524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
427624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
427724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
427824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
427924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA_ID: {
428024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
428124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
428224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
428324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + Data._ID + "=? AND "
428424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
428524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
428624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
428724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
428824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
428924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
4290ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
429124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
429224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
429324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
429424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
429524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4296a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
42974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
429882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
42994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
43004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
43016bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
43026bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
430300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4304a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
43053653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
430682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
43074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
43084da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
43093653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
43103653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
43113653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
43123653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4313a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
4314a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4315a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4316a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
4317a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
4318a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4319a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4320a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4321a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
4322a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
4323a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
4324a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
4325a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
4326a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4327a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
4328a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4329a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
4330a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
4331a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4332a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4333a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
4334a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4335a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4336a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4337a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4338a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
4339a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
4340a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
4341a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
4342a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
4343a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4344a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4345a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4346a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
4347a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
4348a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
4349a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4350a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4351a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
43523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
43533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
43543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
43553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
43563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
43573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
43583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
43593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
43603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemsColumns.CONCRETE_ID + "=?");
43613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
43623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
43633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
43643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
43653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                MatrixCursor cursor = new MatrixCursor(
43663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{StreamItems.MAX_ITEMS, StreamItems.PHOTO_MAX_BYTES}, 1);
43673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                cursor.addRow(
43683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new Object[]{
43693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                MAX_STREAM_ITEMS_PER_RAW_CONTACT,
43703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                mMaxStreamItemPhotoSizeBytes
43713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        });
43723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return cursor;
43733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
43743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
43753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
43763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
43773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
43783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
43793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
43803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
43813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
43823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
43833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
43843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
43853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
43863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
43873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
43883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
43893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
43903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
43913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
43923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
43933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
43943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
43953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
43963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
43973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
43983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
43994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
440082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
440189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
44022815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
44032815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
44042815f58f72f109790585931f601a63ddc02536a5Evan Millar
440548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
440682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
440848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
44094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
441048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
441148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
441248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4413ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
441446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
441546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
441646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
441746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
441846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
441946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
442089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
4421ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
44224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
44234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4424a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
44255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
442645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
44275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
44285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
44295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
4430155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
4431155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
4432155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
4433155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
4434155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
4435155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
4436155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
44372352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
4438155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
44395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
444045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
44415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
44425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4443892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
4444892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
44455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
44465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
44475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
44485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
4449892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
4450892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
4451892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
4452892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
4453892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
445445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
445545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
445645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
445745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
445845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
445945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
446045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
44615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
44625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4463a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
4464ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
44655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
4466a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
446746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
446846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
446946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
447046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
447146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
447246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
4473a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
4474ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4475ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4476ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
44774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
447882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
447989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
44804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
44814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
44824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
448348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
448482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
44864da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
44874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
448848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
448948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
449048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
44915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
449282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
449389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
44944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
449508768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
449608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
449708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
449808768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
44994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
4500ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4501ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4502ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
45035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
450446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
450546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
450646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
450746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
450846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
450946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
451007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
45117d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
451207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
451307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
451407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
451507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
451607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
451707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
45185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
451907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
452007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
452107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
452207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
452307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
452407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
452507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
452607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
452707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
45282a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
45292a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(mDbHelper.getMimeTypeIdForEmail());
45302a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
453107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
453220938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
4533155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
4534155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
4535155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
4536155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
4537155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(mDbHelper.getMimeTypeIdForEmail());
4538155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
4539155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
4540155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
4541155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
4542155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
4543155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
4544155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
45452352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
4546155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
45475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
45485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4549a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
45505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
45515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
4552a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
455346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
455446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
455546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
45567d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
45577d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
45587d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
4559a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
45605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
45615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
45625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4563ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
456482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
456589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
456689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4567ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4568ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4569ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
457048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
457182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
457348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
457448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
45754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
457648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
457748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
457848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
45795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4580763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
458124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true);
45824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
45834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
45844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
45855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
45865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
458724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, false);
4588763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
45894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
45904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
45914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
45924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
45934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
45945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
45955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
459682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
45984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
459924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true);
460024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
460124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
460224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
46033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
46043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
46053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceProfilePermissionForRawContact(rawContactId, false);
46063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
46073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
46083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
46093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
46103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
461124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
461224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS: {
461324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
461424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
461524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
461624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
461724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
461824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
461924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID: {
462024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
462124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = ContentUris.parseId(uri);
462224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
462324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawContacts(qb, uri);
462424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
462524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
462624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
462724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
462824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
462924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
463024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
463124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
463224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
463324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
463424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
463524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + Data.RAW_CONTACT_ID + "=?");
463624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
463724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
463824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
463924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
464024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermission(false);
464124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
464224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
464324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
464424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
464524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        + RawContacts._ID + "=?");
4646e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4647e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4648e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
4649e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
465082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
465124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                appendProfileRestriction(qb, uri, RawContacts.RAW_CONTACT_IS_USER_PROFILE, true);
4652e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4653e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4654e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
46554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
465624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = ContentUris.parseId(uri);
465724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForData(dataId, false);
465882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
46604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
4661a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
4662a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
4663a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
4664a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
46654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4666a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
4667a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
4668a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
4669892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
4670a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
4671a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4672e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
4673e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
4674e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        mDbHelper.getCurrentCountryIso());
4675892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
4676892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
4677892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
4678e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
4679e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
4680e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
4681e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
4682a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
4683a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
4684a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4685ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
4686ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
4687ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
468889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4689ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4690ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4691ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4692ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
4693ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
4694ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
46954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
46964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
4697ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4698ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4699ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4700ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
4701ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS + " AS groups");
4702ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
470389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
470489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
4705ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4706ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4707ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4708b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
47090c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
4710b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
4711b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
4712b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
4713b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
471431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
4715d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
47162d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
47172d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
47182d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
47192d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
472031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
4721d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
4722d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
472331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
472431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
472531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
472631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
47275b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
47285b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
47295b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
47305b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
47315b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
47325b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
47335b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
47345b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
473576dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
47365b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
47375b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
47385b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
47395b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
47405b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
47415b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
47425b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
4743763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
47447581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
47457581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
47465b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
474731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
474831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4749eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
4750eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
4751eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
475289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4753e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4754e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
4755e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
4756b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
4757e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
475882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4759b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
4760e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4761e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
476282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4763b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
4764e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4765e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
4766e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4767eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
4768eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
4769eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
477082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
47710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
47725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
47735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
47745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
477582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
47760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
47774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
47784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
47795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
47805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
47815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
4782c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
4783174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
4784174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, uri, projection, limit);
4785c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4786c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4787c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
47882d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
4789174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
4790174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
4791174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
4792174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        db, projection, lookupKey, filter);
4793c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4794c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
47951b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
4796ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
47971b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
47981b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
47991b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
48001b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
4801ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
48021b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
48031b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
48041b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
48051b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
48061b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
4807ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
48081b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
48091b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
48101b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
48111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
48121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
4813ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
48141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
481571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
48161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
48171b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
48181b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
481946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
4820a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
482146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
482246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
482346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
482446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
482546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
4826a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
48274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
48284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
482946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
483046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
483146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
483209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
483309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
483409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
483509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4836d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
4837d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4838d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4839d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4840d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4841d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4842d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
4843385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
4844d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4845d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4846385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
4847d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
4848d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4849d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4850d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
48517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
48527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
48537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
48547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
48554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
4856f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
4857c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
48584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
48594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
486009e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
48617f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
4862ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
4863ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
4864ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
4865ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
4866ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4867ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
48685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
48695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
48705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
48715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
48725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
4873038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
4874038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
4875038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
4876038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
48775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
48785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
48794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
48804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
48814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
48824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
48834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
48844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
488509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
488609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
488709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
488809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
488909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
489009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
489109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
489209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
489309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
489409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
489509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
489609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
489709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
489809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
489909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
490009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4901a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
4902a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
4903a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
4904a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
4905a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
4906a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
4907a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
4908a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
4909a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
4910a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
4911a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
4912a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
4913a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
4914a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
4915a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
4916a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
4917a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4918a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
4919a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
4920a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
4921a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
4922a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
4923a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
4924a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
4925a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4926a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4927a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
4928a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
4929a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
493009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4931bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
4932bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
4933bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
4934bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
4935ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4936bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4937bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
4938ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
4939ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4940bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
4941bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
4942bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
4943bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
494424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The first letter of the sort key column is what is used for the index headings, except
494524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // in the case of the user's profile, in which case it is empty.
494624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        public static final String SECTION_HEADING_TEMPLATE =
494724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "(CASE WHEN %1$s=1 THEN '' ELSE SUBSTR(%2$s,1,1) END)";
494824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4949de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
4950ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4951ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4952ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
4953ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
4954ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
4955ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
4956ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
4957ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
4958ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
4959ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4960ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
4961ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
4962ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
4963ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
4964ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
496524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
496624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // If the sort order contains one of the "is_profile" columns, we need to strip it out
496724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            // first.
496824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (sortOrder.contains(Contacts.IS_USER_PROFILE)
496924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    || sortOrder.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
497024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                String[] splitOrderClauses = sortOrder.split(",");
497124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                StringBuilder rejoinedClause = new StringBuilder();
497224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                for (String orderClause : splitOrderClauses) {
497324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!orderClause.contains(Contacts.IS_USER_PROFILE)
497424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            && !orderClause.contains(RawContacts.RAW_CONTACT_IS_USER_PROFILE)) {
497524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        if (rejoinedClause.length() > 0) {
497624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                            rejoinedClause.append(", ");
497724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        }
497824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        rejoinedClause.append(orderClause.trim());
497924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    }
498024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                }
498124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sortOrder = rejoinedClause.toString();
498224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
498324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4984ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
4985ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
4986ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
4987ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
4988ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
4989ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
4990ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4991ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
4992ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
4993ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4994ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4995bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
4996ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
499724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
499824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user profile column varies depending on the view.
4999ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        String profileColumn = qb.getTables().contains(Views.CONTACTS)
500024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                ? Contacts.IS_USER_PROFILE
500124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                : RawContacts.RAW_CONTACT_IS_USER_PROFILE;
500224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        String sectionHeading = String.format(
500324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                AddressBookIndexQuery.SECTION_HEADING_TEMPLATE, profileColumn, sortKey);
5004bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
500524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
5006bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5007bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
5008bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
5009bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
5010bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
5011bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
5012bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
5013bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
5014ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
501524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
5016bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
5017ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
5018ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
5019ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
5020ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5021f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
5022ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
5023ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
5024ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5025ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
5026f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
5027ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
5028ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
5029bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
5030bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
5031bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5032bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
5033bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
5034bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
5035ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
5036f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
5037bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
5038bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
5039bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
5040bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
5041bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
5042bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
5043bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
5044bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
5045bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
5046bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
5047bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5048bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
5049bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
5050bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
5051bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
5052bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5053bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
5054bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
5055bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
5056ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5057ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5058e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikov            return new AddressBookCursor((CrossProcessCursor) cursor, titles, counts);
5059ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
5060f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
5061ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5062ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5063ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
50642d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
506592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
506692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
506792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
506892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
50692d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
50702d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
50715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
50725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
50735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
507492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
507592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
507692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
507792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
507892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
507992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
508092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
508192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
508292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
508392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
508492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
508592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
508692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
508792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
508892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
508992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
509092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
509192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
509292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
50935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
50945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
50955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
50975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
50985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
51005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
51015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
51035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
51045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
51055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
51065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
51075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
51085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
51105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
51115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
51125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
51135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
51145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
51165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
51175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
51185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
51195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
51205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
512192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
51225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
51235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
51245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
51255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
51265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
51275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
51285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
51305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
51315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
51325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
51335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
51345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
51355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
51365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
51375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
51385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
51395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
514092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
514192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
51425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
51435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
51445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
51455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
51465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
51475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
51485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
51495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
51505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
51515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
51535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
51545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
515592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
515692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
51575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
51595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
51605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
51615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
516292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
51635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
51645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
51665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
51675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
516892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
51695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
51705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
517192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
517292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
517392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
517492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
51755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
51765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
517792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
517892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
517992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
51805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
51815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
518292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
518392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
51845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
518592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
518692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
518792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
518892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
518992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
519092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
519192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
519292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
519392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
519492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
519592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
519692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
519792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
519892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
519992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
520092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
520192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
520292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
520392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
520492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
520592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
52065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
520892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
520992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
521092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
521192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
521292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
521392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
521492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
521592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
521692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
521792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
521892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
521992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
522092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
522192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
522292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
522392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
522492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
522592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
522692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
522792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
522892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
52295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
52305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
52315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
52325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
523392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
523492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
52355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
52365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
52375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
52385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
52405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
52415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
52425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
52445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
52455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
52465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
52475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
52485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
52495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
52505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
52515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
52525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
52535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
525492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
525592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
525692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
52575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
52585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
52595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
52605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
52615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
52625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
52635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
52645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
52655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
52685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
52695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
527092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
527192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
527292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
527392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
527492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
527592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
527692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
527792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
527892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
527992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
528092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
5281ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
5282ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
5283ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
5284ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
52855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
52865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
52875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
52885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
52895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
52905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
52925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
52935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
52955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
52965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
52985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
52995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
53005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
53015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
53025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
53035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
53045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
53055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
53065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
53075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
53085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
53095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
53105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
53115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
53125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
53145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
53155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
53165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
53175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
53195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5320763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5321763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
53222f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false, false);
53232f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
53242f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
53252f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
53262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * @param forStrequentFrequent Should be used only in strequent handling.
53272f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     *     true when this is for frequently contacted listing (not starred)
53282f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * @param strequentPhoneCallOnly Should be used only in strequent handling.
53292f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     *     true when this is for phone-only results. See also
53302f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * {@link ContactsContract#STREQUENT_PHONE_ONLY}.
53312f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
53322f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
53332f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            String[] projection, boolean forStrequentFrequent, boolean strequentPhoneCallOnly) {
533482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5335ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
53362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
53372f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
53382f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        if (forStrequentFrequent) {
53392f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            final String strequentPhoneCallOnlyClause =
53402f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                (strequentPhoneCallOnly ? DbQueryUtils.concatenateClauses(
53412f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        MimetypesColumns.MIMETYPE + "=\'" + Phone.CONTENT_ITEM_TYPE + "'",
53422f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" +
53432f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        DataUsageStatColumns.USAGE_TYPE_INT_CALL)
53442f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                : "");
53452f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            // Use INNER JOIN for maximum performance, ommiting unnecessary rows as much as
53462f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            // possible.
53472f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            sb.append(" INNER JOIN " +
5348ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                    Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT +
53492f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    " ON (" +
53502f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
53512f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
5352ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID,
53532f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            strequentPhoneCallOnlyClause) +
53542f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
53552f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
53562f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
53577ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
53587ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5359916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5360916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
5361916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5362916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5363916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
5364916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
5365916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
5366916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
5367916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
53687ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov            String[] projection, String filter, long directoryId) {
53697ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
53707ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5371ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
5372916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
537303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
537403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
537503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
537603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
537730cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
537830cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
53795e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
53805e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            appendSearchIndexJoin(sb, uri, projection, filter);
53815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
53827ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
53837ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
538403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
538503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
538603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
5387916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
538803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
538903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            StringBuilder sb, Uri uri, String[] projection, String filter) {
5390916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5391174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET)) {
539203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
539303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
539403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
539503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
539603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
539703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
539803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
53995e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
54005e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
54015e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
54025e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
54035e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
54045e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
54055e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
54065e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
54075e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
5408174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
5409174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens);
5410174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
5411174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(sb, filter, false, null, null, null, 0);
5412174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
5413174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
5414174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
5415174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
5416174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
5417174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            int maxTokens) {
5418174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
5419174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
5420174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
5421174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
5422174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
5423174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
54243716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
54253716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
54263716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        boolean singleTokenSearch = filter.split(QUERY_TOKENIZER_REGEX).length == 1;
54273716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
5428174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
5429174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            emailAddress = mDbHelper.extractAddressFromEmailAddress(filter);
5430174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
5431174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
5432174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
543304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
543404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
543504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
543604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        mDbHelper.getCountryIso());
543704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
5438174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
5439174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
5440174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
5441174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
54425e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
54435e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
54443d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
54455e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
544604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
544704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
544804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
544904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
545004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
545104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
54523d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
54533d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
54543716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
54553716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
54563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
54573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
54583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
54593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
54603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
54613d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
54623d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
54633d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
54643d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
546504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
546604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
546704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
546804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
546904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
547004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
547104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
547204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
547304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
547404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
547504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
547604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
547704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
547804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
547904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
548004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
54815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
54825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
54833716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
54843716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                // Optimization for single-token search.
54853716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                if (singleTokenSearch) {
54863716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
54873716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
54883716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
54893716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
54905e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
549103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
549204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
549304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
54943716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    // Optimization for single-token search.
54953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    if (singleTokenSearch) {
54963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
54973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
54983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
54993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
55003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
55013716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
55023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
55033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
55043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
55053716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
55063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
55073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
55083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
55093716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
55103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
55113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
551204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
551304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
551404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
551503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
55165e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
55175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
551803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
55195e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
55205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
55215e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(Tables.SEARCH_INDEX + " MATCH ");
55225e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
55232352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, "\"" + sanitizeMatch(filter) + "*\"");
55243d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
55252352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb,
552604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    "\"" + sanitizeMatch(filter) + "*\" OR \"" + phoneNumber + "*\""
55272352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                            + (numberE164 != null ? " OR \"" + numberE164 + "\"" : ""));
552803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
55292352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filter) + "*");
55309c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
553103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
5532a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
5533a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
55342352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    private String sanitizeMatch(String filter) {
55352352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        // TODO more robust preprocessing of match expressions
55362352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        return filter.replace('-', ' ').replace('\"', ' ');
55372352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
55382352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
55395e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
55405e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
55415e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
55425e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
55435e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
55445e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
55455e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
55465e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
55475e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
55485e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
55495e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
55505e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
55515e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
55525e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
55535e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
5554763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
5555763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
5556ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
5557763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
5558763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
5559763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
5560763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
5561763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5562a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
5563ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
5564a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
556546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
556646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
556746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
556882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
556982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
557046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
557146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
557246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
557346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
557446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
557546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
557646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
557746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
557846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
557982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5580ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
558182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
558282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
5583a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
5584a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5585a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5586a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
55873296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
558846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
558946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
559046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
559146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
559282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
5593f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
5594f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
5595f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
5596f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
5597f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
559882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
5599ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
5600ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
56010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
56020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
56030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5604ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
56050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
5606a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5607a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
56080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5609a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5610a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
5611a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5612a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
56133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
56143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        StringBuilder sb = new StringBuilder();
56153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        sb.append(Tables.STREAM_ITEMS).append(" JOIN ").append(Tables.RAW_CONTACTS)
56163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                .append(" ON ").append(StreamItemsColumns.CONCRETE_RAW_CONTACT_ID).append("=")
56173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                .append(RawContactsColumns.CONCRETE_ID)
56183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                .append(" JOIN ").append(Tables.CONTACTS)
56193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                .append(" ON ").append(RawContactsColumns.CONCRETE_CONTACT_ID).append("=")
56203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                .append(ContactsColumns.CONCRETE_ID);
56213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setTables(sb.toString());
56223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
56233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
56243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
56253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
56263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        StringBuilder sb = new StringBuilder();
56273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        sb.append(Tables.STREAM_ITEM_PHOTOS).append(" JOIN ").append(Tables.STREAM_ITEMS)
56283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                .append(" ON ").append(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID).append("=")
56293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                .append(StreamItemsColumns.CONCRETE_ID);
56303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setTables(sb.toString());
56313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
56323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
56333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5634a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
5635a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
5636a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5637ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
5638a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
5639a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5640a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
5641a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5642a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
5643a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
5644a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5645a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5646a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
5647a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
5648a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5649a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5650a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
5651a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
5652a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5653a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
5654a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
5655a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
5656a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
5657a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
5658a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
5659a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
5660a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
5661a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
56620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5663a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
56640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5665a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
5666a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5667b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
56680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
56690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
56700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
56710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
56720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
56730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
5674a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
5675a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
56760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5677a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5678a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
567946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
568046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
568146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
568246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
568346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
568446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
5685a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
5686a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
5687a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5688a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
5689a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
5690a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
5691a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
5692a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5693a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5694a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5695a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
5696a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5697a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
5698a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
5699a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
5700a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5701a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5702a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
570324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
5704385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
5705385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
570624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
5707385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
5708385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
570924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
571024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
571124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
571224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
571324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
571424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private void appendProfileRestriction(SQLiteQueryBuilder qb, Uri uri, String profileColumn,
571524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            boolean andRequired) {
571624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (!shouldIncludeProfile(uri)) {
571724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            qb.appendWhere((andRequired ? " AND (" : "")
571824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + profileColumn + " IS NULL OR "
571924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + profileColumn + "=0"
572024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    + (andRequired ? ")" : ""));
5721385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
5722385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
5723385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
572424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private String prependProfileSortIfNeeded(Uri uri, String sortOrder) {
572524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (shouldIncludeProfile(uri)) {
572624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            if (TextUtils.isEmpty(sortOrder)) {
572724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC";
572824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            } else {
572924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                return Contacts.IS_USER_PROFILE + " DESC, " + sortOrder;
573024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
573124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
573224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return sortOrder;
573324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
573424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
573524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean shouldIncludeProfile(Uri uri) {
573624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // The user's profile may be returned alongside other contacts if it was requested and
573724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        // the calling application has permission to read profile data.
5738377850d2dfd28eaf1b22273a50cfe066f6667ab9Dave Santoro        boolean profileRequested = readBooleanQueryParameter(uri, ContactsContract.ALLOW_PROFILE,
573924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                false);
574024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        if (profileRequested) {
574124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            enforceProfilePermission(false);
574224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
574324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return profileRequested;
574424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
574524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
57464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
5747f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5748f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5749e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5750e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5751e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5752e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5753fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5754fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5755e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5756e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5757e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5758e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5759e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5760e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
57614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
57624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
57634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
57644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
57654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
57664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
57674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
57684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
57694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5770e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
5771f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5772f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5773e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5774e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5775e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5776e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5777fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5778fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5779e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5780e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5781e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5782e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5783e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5784e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
5785e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
5786e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
5787e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
5788e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
5789e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
5790e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
5791e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
5792e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
5793e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
5794e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
5795e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
5796e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
5797e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
5798e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
5799e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
58007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5801c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
5802c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
5803c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
5804c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
5805c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
5806f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
58072e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
5808c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
5809c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5810c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5811c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
5812c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
5813c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
5814c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
5815c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
5816c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
5817c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5818c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
5819c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
5820c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
5821c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5822c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5823c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5824c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5825b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
5826f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
5827415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
5828415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
5829415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
5830b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
5831b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
5832a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
583324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
583424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForRawContact(rawContactId, false);
5835f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
583624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
583724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
583824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(rawContactId)});
5839e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
5840b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5841e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
584224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
584324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                enforceProfilePermissionForData(dataId, false);
5844f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
5845e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
584624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
5847d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5848d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5849fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
5850fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
5851fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
5852fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
5853fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5854fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
5855fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
5856fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
585742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
5858fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
585942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
586042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
586142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
586242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5863fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
5864f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
586542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
586642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
586742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
586849d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
586942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
587042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
587142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
5872fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
587342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
5874fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
5875fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                mProfileIdCache.init(mDb, false);
5876fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
5877d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
5878d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
587942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
588042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
5881d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
588242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
5883d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
588442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
588524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    long contactId = lookupContactIdByLookupKey(db, lookupKey);
588624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    enforceProfilePermissionForContact(contactId, false);
588724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
5888fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    if (mProfileIdCache.profileContactId == contactId) {
5889fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                        queryUri = queryUri.buildUpon().appendQueryParameter(
5890377850d2dfd28eaf1b22273a50cfe066f6667ab9Dave Santoro                                ContactsContract.ALLOW_PROFILE, "true").build();
5891fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                    }
589242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
589342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
589442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
589542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
5896d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5897d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
5898d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
5899d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
5900d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5901fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
5902f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
5903d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5904b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5905b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
5906fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
5907fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
5908b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
5909b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
5910b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5911f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,
5912e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
5913e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
5914e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
5915e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
5916e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
5917e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
5918e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5919e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
5920ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
5921e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
5922e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
592308ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
5924f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
5925f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
592608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
592708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
592808ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
592908ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
5930e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
5931e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5932d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
5933d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5934d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5935f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
5936d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
5937d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5938f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
5939d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
5940d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
5941d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5942d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
5943d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5944f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
5945f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
5946f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
5947d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
5948ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
5949ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
5950d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5951d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5952d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5953f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
5954f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
5955f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
5956f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
5957f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
5958f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
5959f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
5960f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
5961d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5962d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
5963d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
5964d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
5965d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5966fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
5967fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
5968d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
5969dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
5970fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
5971fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
5972dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
5973dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
59747a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
5975dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
5976108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
5977108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
5978108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
5979fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null)) {
5980108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
5981108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
5982108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
5983d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5984108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
5985108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
5986108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
5987108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
5988108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
5989108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
5990108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
5991108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
5992108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
5993108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
5994108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
5995108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
5996108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
5997d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5998d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5999d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
6000b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
60014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
60024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
6003415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6004415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
6005415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6006a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
60074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
6008b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
6009be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
60102d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
6011b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
6012b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
601324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
6014b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
6015f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
601642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
601724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
6018f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
6019f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
6020f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                return "image/png";
6021b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
602224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
6023be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
6024b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
602524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
6026b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
6027f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
602824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
6029f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
6030508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
6031b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
603248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
603348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
603448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
603548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
60369005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
60379005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
603848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
603948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
604048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
604148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
604248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
604348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
604448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
604548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
6046b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
6047b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
6048b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
6049b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
6050b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
6051b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
6052b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
6053b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
6054c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
6055c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
6056c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
6057c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
6058d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
6059d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
6060d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
6061d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
606261efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
606361efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
60644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
60654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
60667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
606709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
606809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
606909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
607009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
607109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
607209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
607309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
607409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
607524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
607609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
607709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
60788727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
607924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
60808727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
60818727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
608209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
608309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
608424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
608509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
608609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
608709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
608809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
608924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
609024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
609109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
609209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
609309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
609409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
609509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
609609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
609709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
609809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
609909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
610024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
610109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
610209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
610309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
610409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
610509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
610609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
610709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
610809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
610909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
611009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
611109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
611209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
611309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
611409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
611509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
611609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
611709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
611809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
611909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
612009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
612109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
6122f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
6123f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6124f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
6125f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
6126f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6127f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6128f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6129f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
6130f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
613178fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.insertNameLookup(rawContactId, dataId, lookupType, name);
6132f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6133f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6134f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6135f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
6136d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
6137f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6138f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6139f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
61402d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
6141d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
6142d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
6143d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
6144d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
6145d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
6146d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
6147d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
6148e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
6149916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
6150916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
6151e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
6152e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
61539a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
61549a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
61559a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
61569a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
61579a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
61589a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
61599a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
61609a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
61619a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
61629a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
61639a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
61649a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
61659a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
61669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
61679a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
61684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
61697a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
61707a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
61717a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
61727a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
61737a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
61747a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
61757a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
61767a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
61777a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
61787a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
6179f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
6180f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
61817a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
61827a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
61837a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
61847a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
61857a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
61867a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
61877a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
61887a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
61897a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
61907a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
61917a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
61927a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
61937a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
61947a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
61957a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
61967a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
61977a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
61987a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
61997a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
62007a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
62017a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
62027a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
62037a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
62047a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
62057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
62067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
62077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
62087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
62097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
62107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
62117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
62127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
62137a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
62147a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
62157a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
62167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
62174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
62184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
62194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
6220b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
6221b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
6222b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
6223b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
6224b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
62254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
62264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
6227b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
6228b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
6229b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
6230caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
62315e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
62325e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
62335e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
62345e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
62355e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
62365e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
62375e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
62385e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
62395e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
62405e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
62415e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
6242caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
6243caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
6244caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
62455f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
6246caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
6247caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
6248caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
6249caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
62506f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
6251caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
62526f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
6253caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
6254f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
625573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
625673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
625773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
625873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
6259bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
6260bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
6261bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
6262bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
626373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
626473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
626573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
626673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
626773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
6268627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
6269627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
6270627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
6271627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
627273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
627373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
627473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
6275627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
6276627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
6277627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
6278627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
6279627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
628073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
628173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
628273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
628373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
628473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
628573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
628673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
6287627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
6288b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
6289d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
6290f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
6291f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
6292f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6293f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
6294f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6295f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6296f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6297f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6298f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6299f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
6300f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
6301f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6302f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6303f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6304f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
6305f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6306f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
6307f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
6308f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6309f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6310f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
6311f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
6312f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
6313f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
6314f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
6315f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6316f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6317f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
6318f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
6319f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
6320f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
6321f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6322f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6323f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
6324f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6325f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6326f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
6327f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
6328f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6329f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
6330f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
6331f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
6332f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
6333f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
6334f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
63355fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
63365fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
63375fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
63385fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
63395fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
63405fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
63415fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
63425fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
63435fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
63445fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
63455fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
6346f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6347f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6348f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
6349f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6350f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
6351f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
6352f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6353f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6354f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
6355f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
6356f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
6357f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6358f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6359f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6360f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
6361f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
6362f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
6363f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
6364f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
6365f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6366f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6367f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
6368f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
63695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
63700dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
63710dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
63720dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
63730dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
63740dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
63750dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
63760dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
63770dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
63780dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
6379bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
63800dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
63810dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
63820dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
63830dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
63840dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
63850dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
63860dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
638749d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = mDbHelper.getWritableDatabase();
63880dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
63890dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
63900dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
63910dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
63920dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
63930dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
63940dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
63950dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE,
63960dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
63970dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
63980dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
63990dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
64000dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
64010dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
64020dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
64030dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
64040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
64050dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
64060dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
6407bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mContactAggregator.aggregateInTransaction(mTransactionContext, mDb);
6408bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
64090dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
64100dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
64110dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
64120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
64130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
64140dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
64150dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
64160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
64170dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
64180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
64199a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
64209a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
64219a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
64229a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
64239a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
64249a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
64259a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
64269a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
64279a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
642846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
642946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
643046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
643146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
643246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
643346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
643446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
643546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
643646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
643746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
643846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
643946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
644046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
644146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
644246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
644346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
644446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
644546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
644646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
644746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
644846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
644946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
645046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final Cursor cursor = mDb.query(
6451ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                Views.DATA,
645246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
645346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
645446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
645546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
645646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
645746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
645846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
645946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.update(Tables.CONTACTS, values2, Contacts._ID + "=?", mSelectionArgs1);
646046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
646146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
646246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
646346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
646446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
646546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
646646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
646746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
646846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
646946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
647046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
647146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
647246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
647346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
647446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
647546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private int updateDataUsageStat(
647646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            ArrayList<Long> dataIds, String type, long currentTimeMillis) {
647746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
647846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
647946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
648046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
648146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
648246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
648346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
648446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
648546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            mDb.beginTransaction();
648646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
648746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                final Cursor cursor = mDb.query(Tables.DATA_USAGE_STAT, columns, where, args,
648846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        null, null, null);
648946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
649046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
649146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
649246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
649346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
649446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
649546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
649646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
649746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
649846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            mDb.update(Tables.DATA_USAGE_STAT, values,
649946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
650046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
650146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
650246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
650346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
650446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
650546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
650646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
650746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
650846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        mDb.insert(Tables.DATA_USAGE_STAT, null, values);
650946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
651046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    mDb.setTransactionSuccessful();
651146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
651246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
651346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
651446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
651546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mDb.endTransaction();
651646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
651746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
651846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
651946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
652046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
652146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
652246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
652346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
652446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
652546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
652646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
652746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
652846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
652946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
653046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
653146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
653246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
653346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
653446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
653546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
653646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
653746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
653846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
653946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
654046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
654146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
654246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
654346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
654446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
654546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
654646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
654746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
654846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
654946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
65504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
6551