ContactsProvider2.java revision dfab50ecd585e55769dea451cb3a47ff69b8b86d
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
1947106a03cd4587123f4fa24f3620baf55fed15d9Dave Santoroimport com.android.common.content.ProjectionMap;
2053214b3ed12b0ff9cb589b6559311f2ac142f2e3Bjorn Bringertimport com.android.common.content.SyncStateContentProviderHelper;
2197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
229d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
2397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
2497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
2697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
2797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
2897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
2971340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
309ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
3223ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.Joins;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
361dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport com.android.providers.contacts.ContactsDatabaseHelper.PhotoFilesColumns;
3797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
389d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.Projections;
3997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
4003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
4197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
4297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.StreamItemPhotosColumns;
44f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport com.android.providers.contacts.ContactsDatabaseHelper.StreamItemsColumns;
4597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
469d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.ViewGroupsColumns;
47ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.Views;
48d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmannimport com.android.providers.contacts.SearchIndexManager.FtsQueryBuilder;
4949ed71913609193a00059df944f6259e9397b0bdMakoto Onukiimport com.android.providers.contacts.aggregation.ContactAggregator;
5049ed71913609193a00059df944f6259e9397b0bdMakoto Onukiimport com.android.providers.contacts.aggregation.ContactAggregator.AggregationSuggestionParameter;
5181567f4a0f7c9c338506bd82f4d33e83c2ccf159Makoto Onukiimport com.android.providers.contacts.aggregation.util.CommonNicknameCache;
5249ed71913609193a00059df944f6259e9397b0bdMakoto Onukiimport com.android.providers.contacts.aggregation.ProfileAggregator;
53dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onukiimport com.android.providers.contacts.util.Clock;
542f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawaimport com.android.providers.contacts.util.DbQueryUtils;
5510178e5e0b9de566e04508b624a89860c61787d6Makoto Onukiimport com.android.providers.contacts.util.NeededForTesting;
5697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardComposer;
5797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardConfig;
5897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
5997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
6097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
61f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawaimport com.google.common.annotations.VisibleForTesting;
62084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onukiimport com.google.common.base.Preconditions;
6397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
64b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
65caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
665b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
67c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
687898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brownimport android.content.CancellationSignal;
69568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
70568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
716ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
7235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
7367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
7467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
75627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
76568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
773d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
78627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
7967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
800bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.pm.PackageManager;
810bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.pm.PackageManager.NameNotFoundException;
825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoroimport android.content.pm.ProviderInfo;
83f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.content.res.Resources;
850bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.res.Resources.NotFoundException;
86409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onukiimport android.database.AbstractCursor;
874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
88ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
8909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
9009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
9208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
94f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.Bitmap;
95f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.BitmapFactory;
964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
97d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
98c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.AsyncTask;
99bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Binder;
1006ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
101bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Handler;
102bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.HandlerThread;
103bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Message;
104ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
105c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.ParcelFileDescriptor.AutoCloseInputStream;
106bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Process;
107b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
10815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.os.StrictMode;
1090dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
1100e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
1113d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
112508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
1133de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
114b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
11582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoroimport android.provider.ContactsContract.Authorization;
11697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
11797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
11897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
11997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
1206d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Note;
12197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
12297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
12397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
1244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
12597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
12697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
127ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
1283de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1295b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1303de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
13171340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport android.provider.ContactsContract.DataUsageFeedback;
132d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
133f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.DisplayPhoto;
1343de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
1353de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
1361dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport android.provider.ContactsContract.PhotoFiles;
1370c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoroimport android.provider.ContactsContract.Profile;
13809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1393de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
1403711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenenimport android.provider.ContactsContract.RawContactsEntity;
141916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1423de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
14382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
1443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.provider.ContactsContract.StreamItemPhotos;
145f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.StreamItems;
14697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
14797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
148a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
1499a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport android.telephony.TelephonyManager;
150a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
151c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
153108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.BufferedWriter;
154d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
155f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.File;
15635997f3fdee2984b6d5373326110eda26929001aMakoto Onukiimport java.io.FileDescriptor;
157b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
158d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
159d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
160108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.OutputStreamWriter;
16135997f3fdee2984b6d5373326110eda26929001aMakoto Onukiimport java.io.PrintWriter;
162108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.Writer;
163d0eb93009559d095de0448907527aeb059801dc4Dave Santoroimport java.security.SecureRandom;
16442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
16742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
168b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1690e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
171622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
172b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1730e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
174ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
180078f588cef389358adabc579de00747878f3c108Dave Santoropublic class ContactsProvider2 extends AbstractContactsProvider
181078f588cef389358adabc579de00747878f3c108Dave Santoro        implements OnAccountsUpdateListener {
182caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
18315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_INITIALIZE = 0;
18415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_OPEN_WRITE_ACCESS = 1;
18515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
18615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
18715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
18805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
18905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
19005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
19105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
192f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int BACKGROUND_TASK_CLEANUP_PHOTOS = 10;
193619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Limit for the maximum number of social stream items to store under a raw contact. */
1983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int MAX_STREAM_ITEMS_PER_RAW_CONTACT = 5;
1993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
200f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** Rate limit (in ms) for photo cleanup.  Do it at most once per day. */
201f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_CLEANUP_RATE_LIMIT = 24 * 60 * 60 * 1000;
202f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
2033d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
20482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Default expiration duration for pre-authorized URIs.  May be overridden from a secure
20582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * setting.
20682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
20782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private static final int DEFAULT_PREAUTHORIZED_URI_EXPIRATION = 5 * 60 * 1000;
20882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
20982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
21082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Random URI parameter that will be appended to preauthorized URIs for uniqueness.
21182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
21282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private static final String PREAUTHORIZED_URI_TOKEN = "perm_token";
21382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
21451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
2153d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
2170dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
2180e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
2190e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
220b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki    /**
221b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki     * If set to "1", we don't remove account data when accounts have been removed.
222b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki     *
223b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki     * This should be used sparingly; even though there are data still available, the UI
224b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki     * don't know anything about them, so they won't show up in the contact filter screen, and
225b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki     * the contact card/editor may get confused to see unknown custom mimetypes.
226b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki     *
227b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki     * We can't spell it out because a property name must be less than 32 chars.
228b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki     */
229b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki    private static final String DEBUG_PROPERTY_KEEP_STALE_ACCOUNT_DATA =
230b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki            "debug.contacts.ksad";
231b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki
2325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final ProfileAwareUriMatcher sUriMatcher =
2335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ProfileAwareUriMatcher(UriMatcher.NO_MATCH);
2344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2352f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
2362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * Used to insert a column into strequent results, which enables SQL to sort the list using
2372f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * the total times contacted. See also {@link #sStrequentFrequentProjectionMap}.
2382f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
2392f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private static final String TIMES_USED_SORT_COLUMN = "times_used_sort";
2405e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
24145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final String FREQUENT_ORDER_BY = DataUsageStatColumns.TIMES_USED + " DESC,"
24245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
24345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
2446e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
2459b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
246dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            " ifnull(" + Contacts.TIMES_CONTACTED + ",0)+1" +
247dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            " WHERE " + Contacts._ID + "=?";
2489b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2496e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2509b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
251dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            " ifnull(" + RawContacts.TIMES_CONTACTED + ",0)+1 " +
252dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            " WHERE " + RawContacts.CONTACT_ID + "=?";
2539b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
254de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
255de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
260d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
261d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
264a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
269a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
270bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_PHOTO = 1010;
271bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_PHOTO = 1011;
272bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_DISPLAY_PHOTO = 1012;
273bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DISPLAY_PHOTO = 1013;
274bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DISPLAY_PHOTO = 1014;
275bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_VCARD = 1015;
276bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_MULTI_VCARD = 1016;
277bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DATA = 1017;
278bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DATA = 1018;
279bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_ENTITIES = 1019;
280bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ENTITIES = 1020;
281bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1021;
282bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_STREAM_ITEMS = 1022;
283bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_STREAM_ITEMS = 1023;
284bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_STREAM_ITEMS = 1024;
285bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_FREQUENT = 1025;
286b6186821548995dce533ee502e82e9abf4c0aadcMakoto Onuki    private static final int CONTACTS_DELETE_USAGE = 1026;
2874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
290193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private static final int RAW_CONTACTS_ID_DATA = 2004;
291193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private static final int RAW_CONTACT_ID_ENTITY = 2005;
292f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_DISPLAY_PHOTO = 2006;
293f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2007;
29482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS_ID = 2008;
2954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2966bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
298ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
29948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
30048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
30148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
30248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
30348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
30448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
30548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
30648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
307e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa    private static final int CALLABLES = 3011;
308e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa    private static final int CALLABLES_ID = 3012;
309e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa    private static final int CALLABLES_FILTER = 3013;
310a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
3126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
313b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
314b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
315b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
31682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
31782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
3181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
31931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
32031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
321eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
322eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
323ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
324ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
325ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
326ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
32735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
328b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
3295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE = 11002;
3305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE_ID = 11003;
33135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
332c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
333c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
334c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
33546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
33646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
33709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
33809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
339d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
340d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
341d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
3437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
34424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
34524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
34624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
34724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
34824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
34924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
35024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
35124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
35224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
3535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_STATUS_UPDATES = 19009;
3543202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro    private static final int PROFILE_RAW_CONTACT_ENTITIES = 19010;
35585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro    private static final int PROFILE_PHOTO = 19011;
35685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro    private static final int PROFILE_DISPLAY_PHOTO = 19012;
35724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
35846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
35946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
3603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS = 21000;
3613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_PHOTOS = 21001;
3623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID = 21002;
3633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS = 21003;
3643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
3653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_LIMIT = 21005;
3663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
367193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private static final int DISPLAY_PHOTO_ID = 22000;
368f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_DIMENSIONS = 22001;
369f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
3705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Inserts into URIs in this map will direct to the profile database if the parent record's
3715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // value (looked up from the ContentValues object with the key specified by the value in this
3725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // map) is in the profile ID-space (see {@link ProfileDatabaseHelper#PROFILE_ID_SPACE}).
3735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final Map<Integer, String> INSERT_URI_ID_VALUE_MAP = Maps.newHashMap();
3745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    static {
3755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(DATA, Data.RAW_CONTACT_ID);
376193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_ID_DATA, Data.RAW_CONTACT_ID);
3775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STATUS_UPDATES, StatusUpdates.DATA_ID);
3785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_ID_STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_ID_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
3835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
38436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // Any interactions that involve these URIs will also require the calling package to have either
38536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // android.permission.READ_SOCIAL_STREAM permission or android.permission.WRITE_SOCIAL_STREAM
38636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // permission, depending on the type of operation being performed.
38736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private static final List<Integer> SOCIAL_STREAM_URIS = Lists.newArrayList(
38836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_ID_STREAM_ITEMS,
38936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_LOOKUP_STREAM_ITEMS,
39036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_LOOKUP_ID_STREAM_ITEMS,
39136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            RAW_CONTACTS_ID_STREAM_ITEMS,
39236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            RAW_CONTACTS_ID_STREAM_ITEMS_ID,
39336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS,
39436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_PHOTOS,
39536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID,
39636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID_PHOTOS,
39736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID_PHOTOS_ID
39836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    );
39936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
400dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
401dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
4029d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                + GroupsColumns.CONCRETE_ACCOUNT_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
4039d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                + " AND " + Groups.FAVORITES + " != 0";
404dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
405dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
406dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
4079d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                + GroupsColumns.CONCRETE_ACCOUNT_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
4089d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                + " AND " + Groups.AUTO_ADD + " != 0";
409dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
410dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
411dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
412dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
413dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
414dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
415dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
416dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
417dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
418dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
419dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
420dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
421d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
422f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
423f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
4249d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                + "JOIN " + Tables.ACCOUNTS + " ON ("
4259d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    + AccountsColumns.CONCRETE_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
4269d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    + ")"
427f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
42867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
42967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
4306cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
4319d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            AccountsColumns.CONCRETE_ACCOUNT_TYPE,
4329d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            AccountsColumns.CONCRETE_ACCOUNT_NAME,
4339d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            AccountsColumns.CONCRETE_DATA_SET,
4343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
435f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
436ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
437ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
438d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
4396802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_TYPE = 1;
4406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_NAME = 2;
44143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_SET = 3;
44243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_ID = 4;
44343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int CONTACT_ID = 5;
444ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
4451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
446f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
4479d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        String TABLE = Tables.RAW_CONTACTS_JOIN_ACCOUNTS;
44819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
44919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
450ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
4519d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                RawContactsColumns.ACCOUNT_ID,
4529d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                AccountsColumns.CONCRETE_ACCOUNT_TYPE,
4539d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                AccountsColumns.CONCRETE_ACCOUNT_NAME,
4549d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                AccountsColumns.CONCRETE_DATA_SET,
45519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
45619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
45719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
4589d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        int ACCOUNT_ID = 1;
4599d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        int ACCOUNT_TYPE = 2;
4609d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        int ACCOUNT_NAME = 3;
4619d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        int DATA_SET = 4;
46219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
46319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
464c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
465caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
46671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
46771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
46871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
46971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
47071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
47171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
47271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
47371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
4747cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
4757cf50494501938f175d288077145acf49da8f171Daniel Lehmann                                    + " AND " + GroupMembership.GROUP_ROW_ID + "="
47671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
47771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
47871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
47971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
480a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
481a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
482a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
483a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
484a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
485a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
486a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
487a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
488a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
489a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
490a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
491a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
492c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
493c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
494c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
495c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
496c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
497c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
498f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    private static final String TIME_SINCE_LAST_USED =
499f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            "(strftime('%s', 'now') - " + DataUsageStatColumns.LAST_TIME_USED + "/1000)";
500f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa
501c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
502c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
5034c4ea154ba08f2c772d645a092c3ffa4497295dfDaisuke Miyakawa     * Within the two groups:
5044c4ea154ba08f2c772d645a092c3ffa4497295dfDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly recently contacted, then the rest.
5054c4ea154ba08f2c772d645a092c3ffa4497295dfDaisuke Miyakawa     * Within each of the bucket - descending count of times contacted (both for data row and for
5064c4ea154ba08f2c772d645a092c3ffa4497295dfDaisuke Miyakawa     * contact row).
5074c4ea154ba08f2c772d645a092c3ffa4497295dfDaisuke Miyakawa     * If all else fails, in_visible_group, alphabetical.
50846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
509c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
510c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
5112262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
512f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa        + "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
51346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
514f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa                + " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
51546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
51646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
51746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
5184c4ea154ba08f2c772d645a092c3ffa4497295dfDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
51946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
52046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
521c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
522c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
52346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
52446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
52546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
526c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
527916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
528916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
529916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
530916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
53192ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
532916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
533f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
534f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
535f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
536f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
537f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
538f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
539f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
540f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
541f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
542f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
54343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.DATA_SET,
54443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
545f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
546f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
547f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
548f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
549f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
550916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
562f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            .add(Contacts.PHOTO_FILE_ID)
5633d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
5643d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
570cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
59103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
59743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.DATA_SET)
59843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.ACCOUNT_TYPE_AND_DATA_SET)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
667038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
672e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
67724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
682916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
687916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6885e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6912f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6962f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
698f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. Right now Starred part just returns NULL for
7024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * those data columns (frequent part should return real ones in data table).
7034928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7044928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyStarredProjectionMap
7054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
7084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER, "NULL")
7094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE, "NULL")
7104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL, "NULL")
7114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
7134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. We hard-code {@link Contacts#IS_USER_PROFILE} to NULL,
7164928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * because sContactsProjectionMap specifies a field that doesn't exist in the view behind the
7174928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * query that uses this projection map.
7184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyFrequentProjectionMap
7204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, DataUsageStatColumns.CONCRETE_TIMES_USED)
7234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER)
7244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE)
7254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL)
7264928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Contacts.IS_USER_PROFILE, "NULL")
7274928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
729f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
731fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
736ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
75424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
759a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
76624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
772a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
78024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
78958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    /** Contains columns in PhoneLookup which are not contained in the data view. */
79058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private static final ProjectionMap sSipLookupColumns = ProjectionMap.builder()
79158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.NUMBER, SipAddress.SIP_ADDRESS)
79258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.TYPE, "0")
79358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.LABEL, "NULL")
79458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.NORMALIZED_NUMBER, "NULL")
79558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .build();
79658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
7974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
80324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
81158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    /** Contains columns from the data view used for SIP address lookup. */
81258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private static final ProjectionMap sDataSipLookupProjectionMap = ProjectionMap.builder()
81358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sDataProjectionMap)
81458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sSipLookupColumns)
81558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .build();
81658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
8175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
82124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
823f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
824f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
825f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
826f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
827f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
82858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    /** Contains columns from the data view used for SIP address lookup. */
82958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private static final ProjectionMap sDistinctDataSipLookupProjectionMap = ProjectionMap.builder()
83058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sDistinctDataProjectionMap)
83158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sSipLookupColumns)
83258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .build();
83358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
8349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
835f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
838f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
839f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
840f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
841f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
8443d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
8453d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
846f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
847f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
848f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
849f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
850f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
851f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
8522530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
853f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
854f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
855ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
856f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
857f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
858f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
859f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
86043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.DATA_SET)
86143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.ACCOUNT_TYPE_AND_DATA_SET)
862f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
863f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
864f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
865f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
866f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
867f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
868f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
869f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
870f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
871f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
872f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
873f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
874f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
875c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
876f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
877f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
878f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
879f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
880f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
881f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
88223ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki    /**
88323ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * Contains {@link Groups} columns along with summary details.
88423ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     *
88523ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * Note {@link Groups#SUMMARY_COUNT} doesn't exist in groups/view_groups.
88623ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * When we detect this column being requested, we join {@link Joins#GROUP_MEMBER_COUNT} to
88723ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * generate it.
88818b09495f5f37b38ff2e1c965e087dfde68c27fbMakoto Onuki     *
88918b09495f5f37b38ff2e1c965e087dfde68c27fbMakoto Onuki     * TODO Support SUMMARY_GROUP_COUNT_PER_ACCOUNT too.  See also queryLocal().
89023ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     */
891f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
892f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
89323ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki            .add(Groups.SUMMARY_COUNT, "ifnull(group_member_count, 0)")
894f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
895f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
896f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
897f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " WHERE " + Contacts.HAS_PHONE_NUMBER + ")")
89818b09495f5f37b38ff2e1c965e087dfde68c27fbMakoto Onuki            .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, "0") // Always returns 0 for now.
899f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
900f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
901373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
902f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
903f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
904f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
905f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
906f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
907f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
908f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
909eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
910f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
911f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
912f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
913f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            .add(Settings.DATA_SET)
914f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
915f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
916f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
917f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
918f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
919f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
920f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
921f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
922f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
9239d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                            + " FROM " + Views.GROUPS
9249d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                            + " WHERE " + ViewGroupsColumns.CONCRETE_ACCOUNT_NAME + "="
925f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
9269d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                + " AND " + ViewGroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
927f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE
9289d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                + " AND ((" + ViewGroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
929f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + " IS NULL) OR ("
9309d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                    + ViewGroupsColumns.CONCRETE_DATA_SET + "="
931f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + "))))=0"
932f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
933f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
934f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
935f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
936f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
937f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
938f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
939f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
940f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
941f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
942f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
943f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
944f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
945f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
946f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
947f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
948f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
949f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
950f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
951f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
95282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
953f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
954f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
955f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
956f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
957f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
958f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
959f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
960f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
961f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
962f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
963f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
964f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
965f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
966f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
967f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
968f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
969f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
970f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
971f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
972f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
973f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
9743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
9753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
9769b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems._ID)
9779b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.CONTACT_ID)
978af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann            .add(StreamItems.CONTACT_LOOKUP_KEY)
9799b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_NAME)
9809b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_TYPE)
9819b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.DATA_SET)
9823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9839b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID)
9843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
9853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
9863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
9873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
9883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
9893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
9900bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC1)
9910bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC2)
9920bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC3)
9930bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC4)
9943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
9953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
9963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
9973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
9983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9990bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID, RawContactsColumns.CONCRETE_SOURCE_ID)
10003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
10013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
10026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_FILE_ID)
10036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_URI,
10046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    "'" + DisplayPhoto.CONTENT_URI + "'||'/'||" + StreamItemPhotos.PHOTO_FILE_ID)
10051dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.HEIGHT)
10061dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.WIDTH)
10071dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.FILESIZE)
10080bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC1)
10090bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC2)
10100bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC3)
10110bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC4)
10123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
10133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1014d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
1015f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
1016f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
1017f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
1018f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
1019f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
1020f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
1021f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
1022f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
1023f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
1024778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
1025778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
1026f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
10277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
10299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
10309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
10319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
10329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
10339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
10342526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
10352526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1036bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1037bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
1038bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1039bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
104051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
104103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
104203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
104303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
104403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
104503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
1046193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private boolean mIsPhoneInitialized;
1047193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private boolean mIsPhone;
10489a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
1049193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private final StringBuilder mSb = new StringBuilder();
1050193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private final String[] mSelectionArgs1 = new String[1];
1051193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private final String[] mSelectionArgs2 = new String[2];
1052dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki    private final String[] mSelectionArgs3 = new String[3];
1053dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki    private final String[] mSelectionArgs4 = new String[4];
1054193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private final ArrayList<String> mSelectionArgs = Lists.newArrayList();
10552526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1056f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
1057f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
10584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
10594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
1060a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
1061d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
1062d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
1063a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
1064a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
10653653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
10663653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
10672d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
10682d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
1069a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
1070f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
1071f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_ID_DISPLAY_PHOTO);
10723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
10733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
1074c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
10755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
10765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
10772149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
1078bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/photo",
1079bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_PHOTO);
10805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
10812149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
10822149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
1083bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/photo",
1084bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_ID_PHOTO);
1085f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
1086f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_DISPLAY_PHOTO);
1087f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
1088f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
1089a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
1090a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
1091a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
1092a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
10933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
10943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
10953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
10963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
1097f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
109842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
109942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
11005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
1101ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
1102ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
11035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
110445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
1105b6186821548995dce533ee502e82e9abf4c0aadcMakoto Onuki        matcher.addURI(ContactsContract.AUTHORITY, "contacts/delete_usage", CONTACTS_DELETE_USAGE);
11063653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
11075ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
11085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
1109193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_ID_DATA);
1110f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
1111f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                RAW_CONTACTS_ID_DISPLAY_PHOTO);
1112193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ID_ENTITY);
11133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
11143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
111582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items/#",
111682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                RAW_CONTACTS_ID_STREAM_ITEMS_ID);
111746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
111846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
1119b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
11204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
11214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
1122ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
112348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
11245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
1125ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
11264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
112748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
11281dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
11295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
11305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
11314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
1132ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
113348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
113446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
113546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
1136e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/callables/", CALLABLES);
1137e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/callables/#", CALLABLES_ID);
1138e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/callables/filter", CALLABLES_FILTER);
1139e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/callables/filter/*", CALLABLES_FILTER);
11401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1141ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1142ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1143ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1144ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
114535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1146b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1147b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
11485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/" + SyncStateContentProviderHelper.PATH,
11495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE);
11505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY,
11515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                "profile/" + SyncStateContentProviderHelper.PATH + "/#",
11525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE_ID);
115335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1154a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1155b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1156b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1157b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1158b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
11594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1160eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1161eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
116282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
116382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
11641f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1165c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1166c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1167c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1168c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
11692d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1170c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1171c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
117209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1173d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1174d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1175d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
11767a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
11777a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
117824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
117924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
118024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
118124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
118224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
118385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/photo", PROFILE_PHOTO);
118485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/display_photo", PROFILE_DISPLAY_PHOTO);
118524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
118624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
118724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
118824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
118924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
119024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
119124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
119224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
11935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/status_updates",
11945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_STATUS_UPDATES);
11953202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contact_entities",
11963202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro                PROFILE_RAW_CONTACT_ENTITIES);
119746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
11983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
11993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
12003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
12013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
12023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
12033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
12043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
12053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1206193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki        matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", DISPLAY_PHOTO_ID);
1207f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
120819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
120919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1210d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1211d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1212d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1213d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1214d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1215d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1216d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1217d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1218d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
12194458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
12204458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1221d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
12223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
12239d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     * An entry in group id cache.
12249d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     *
12259d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     * TODO: Move this and {@link #mGroupIdCache} to {@link DataRowHandlerForGroupMembership}.
1226ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1227e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
12289d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        long accountId;
1229ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1230ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1231ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1232a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
12339d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    /**
12349d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     * Map from group source IDs to lists of {@link GroupIdCacheEntry}s.
12359d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     *
12369d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     * We don't need a soft cache for groups - the assumption is that there will only
12379d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     * be a small number of contact groups. The cache is keyed off source id.  The value
12389d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     * is a list of groups with this group id.
12399d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     */
1240e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1241e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
124224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
12435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Sub-provider for handling profile requests against the profile database.
12445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
12455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileProvider mProfileProvider;
1246f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12474097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1248f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1249315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1250622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1251622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
125272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
12535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1254078f588cef389358adabc579de00747878f3c108Dave Santoro    // The database tag to use for representing the contacts DB in contacts transactions.
1255078f588cef389358adabc579de00747878f3c108Dave Santoro    /* package */ static final String CONTACTS_DB_TAG = "contacts";
1256078f588cef389358adabc579de00747878f3c108Dave Santoro
1257078f588cef389358adabc579de00747878f3c108Dave Santoro    // The database tag to use for representing the profile DB in contacts transactions.
1258078f588cef389358adabc579de00747878f3c108Dave Santoro    /* package */ static final String PROFILE_DB_TAG = "profile";
1259078f588cef389358adabc579de00747878f3c108Dave Santoro
12605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
12615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * The active (thread-local) database.  This will be switched between a contacts-specific
12625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database and a profile-specific database, depending on what the current operation is
12635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * targeted to.
12645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
12655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<SQLiteDatabase> mActiveDb = new ThreadLocal<SQLiteDatabase>();
12665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12676efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    /**
12686efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * The thread-local holder of the active transaction.  Shared between this and the profile
12696efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * provider, to keep transactions on both databases synchronized.
12706efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     */
12716efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    private final ThreadLocal<ContactsTransaction> mTransactionHolder =
12726efb7db26598b105342d02207e0ca1c8725c10daDave Santoro            new ThreadLocal<ContactsTransaction>();
12736efb7db26598b105342d02207e0ca1c8725c10daDave Santoro
12745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // This variable keeps track of whether the current operation is intended for the profile DB.
12755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<Boolean> mInProfileMode = new ThreadLocal<Boolean>();
12765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Separate data row handler instances for contact data and profile data.
12785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mDataRowHandlers;
12795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mProfileDataRowHandlers;
12805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile, we will use one of two
12825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // database helper instances.
12835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactsDatabaseHelper> mDbHelper =
12845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<ContactsDatabaseHelper>();
12855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactsDatabaseHelper mContactsHelper;
12865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileDatabaseHelper mProfileHelper;
12875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
12895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two aggregator instances.
12905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactAggregator> mAggregator = new ThreadLocal<ContactAggregator>();
1291622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
12925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactAggregator mProfileAggregator;
12935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
12945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
12955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two photo store instances (with their files stored in separate subdirectories).
12965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<PhotoStore> mPhotoStore = new ThreadLocal<PhotoStore>();
12975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mContactsPhotoStore;
12985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mProfilePhotoStore;
12995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // The active transaction context will switch depending on the operation being performed.
13015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Both transaction contexts will be cleared out when a batch transaction is started, and
13025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // each will be processed separately when a batch transaction completes.
1303193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private final TransactionContext mContactTransactionContext = new TransactionContext(false);
1304193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private final TransactionContext mProfileTransactionContext = new TransactionContext(true);
13055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<TransactionContext> mTransactionContext =
13065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<TransactionContext>();
13075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
130882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    // Duration in milliseconds that pre-authorized URIs will remain valid.
130982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private long mPreAuthorizedUriDuration;
131082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
131182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    // Map of single-use pre-authorized URIs to expiration times.
1312193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private final Map<Uri, Long> mPreAuthorizedUris = Maps.newHashMap();
131382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
1314d0eb93009559d095de0448907527aeb059801dc4Dave Santoro    // Random number generator.
1315193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private final SecureRandom mRandom = new SecureRandom();
131682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
1317f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1318a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1319d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1320f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1321a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1322193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private final ContentValues mValues = new ContentValues();
1323193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    private final HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
132420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
132509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
13263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
132709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
132815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
132915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
133015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1331bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
133273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
13331a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
13341a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
133581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
133681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
13374cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
13383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1339d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1340bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1341bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1342bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1343f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mLastPhotoCleanup = 0;
1344f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
134535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    private FastScrollingIndexCache mFastScrollingIndexCache;
134635997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
134735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    // Stats about FastScrollingIndex.
134835997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    private int mFastScrollingIndexCacheRequestCount;
134935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    private int mFastScrollingIndexCacheMissCount;
135035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    private long mTotalTimeFastScrollingIndexGenerate;
135135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
13524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
13534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1354663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1355663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate start");
1356663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        }
1357de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1358ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1359ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1360ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1361ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1362887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki            // In production code we don't want to throw here, so that phone will still work
1363887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki            // in low storage situations.
1364887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki            // See I5c88a3024ff1c5a06b5756b29a2d903f8f6a2531
1365887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki            if (shouldThrowExceptionForInitializationError()) {
1366887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki                throw e;
1367887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki            }
1368ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1369663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        } finally {
1370663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1371663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki                Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate finish");
1372663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            }
1373ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1374ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
137535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1376887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki    protected boolean shouldThrowExceptionForInitializationError() {
1377887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki        return false;
1378887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki    }
1379887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki
1380ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
138115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
138215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
138315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
138435997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        mFastScrollingIndexCache = new FastScrollingIndexCache(getContext());
138535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
1386078f588cef389358adabc579de00747878f3c108Dave Santoro        mContactsHelper = getDatabaseHelper(getContext());
13875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
1388078f588cef389358adabc579de00747878f3c108Dave Santoro
1389078f588cef389358adabc579de00747878f3c108Dave Santoro        // Set up the DB helper for keeping transactions serialized.
1390078f588cef389358adabc579de00747878f3c108Dave Santoro        setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG);
1391078f588cef389358adabc579de00747878f3c108Dave Santoro
139272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1393a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
139465ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1395bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
139615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
139715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
139872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1399bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1400bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1401bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1402bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1403bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1404bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1405bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1406bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1407bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
14082a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
14095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set up the sub-provider for handling profiles.
14105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider = getProfileProvider();
1411c990980ab4beb7b81c3337526f1bdcd5d1a14730Dave Santoro        mProfileProvider.setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG);
14125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        ProviderInfo profileInfo = new ProviderInfo();
14135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.readPermission = "android.permission.READ_PROFILE";
14145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.writePermission = "android.permission.WRITE_PROFILE";
14155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider.attachInfo(getContext(), profileInfo);
1416078f588cef389358adabc579de00747878f3c108Dave Santoro        mProfileHelper = mProfileProvider.getDatabaseHelper(getContext());
14175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
141882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        // Initialize the pre-authorized URI duration.
141982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        mPreAuthorizedUriDuration = android.provider.Settings.Secure.getLong(
142082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                getContext().getContentResolver(),
142182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                android.provider.Settings.Secure.CONTACTS_PREAUTH_URI_EXPIRATION,
142282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                DEFAULT_PREAUTHORIZED_URI_EXPIRATION);
142382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
142415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1425bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1426bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1427bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
142805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1429bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
143015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
1431f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
14323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
143349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
14344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
14354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1436767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
143751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
143851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
143904b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
144015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
14415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mLegacyApiSupport = new LegacyApiSupport(context, mContactsHelper, this,
14425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mGlobalSearchSupport);
14434cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
14445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mNameSplitter = mContactsHelper.createNameSplitter();
14454cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
14464cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
14475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mCommonNicknameCache = new CommonNicknameCache(mContactsHelper.getReadableDatabase());
1448cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
14495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactAggregator = new ContactAggregator(this, mContactsHelper,
145015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14515b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
14525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator = new ProfileAggregator(this, mProfileHelper,
14535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1455f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
14565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore = new PhotoStore(getContext().getFilesDir(), mContactsHelper);
14585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore = new PhotoStore(new File(getContext().getFilesDir(), "profile"),
14595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileHelper);
14605b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1461bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
14625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mDataRowHandlers, mContactsHelper, mContactAggregator,
14635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsPhotoStore);
14645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileDataRowHandlers = new HashMap<String, DataRowHandler>();
14655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mProfileDataRowHandlers, mProfileHelper, mProfileAggregator,
14665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfilePhotoStore);
14675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set initial thread-local state variables for the Contacts DB.
14695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
14705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
1471bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
14725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private void initDataRowHandlers(Map<String, DataRowHandler> handlerMap,
14735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            ContactsDatabaseHelper dbHelper, ContactAggregator contactAggregator,
14745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            PhotoStore photoStore) {
14755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Context context = getContext();
14765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Email.CONTENT_ITEM_TYPE,
14775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForEmail(context, dbHelper, contactAggregator));
14785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Im.CONTENT_ITEM_TYPE,
14795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForIm(context, dbHelper, contactAggregator));
14805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Organization.CONTENT_ITEM_TYPE,
14815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForOrganization(context, dbHelper, contactAggregator));
14825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Phone.CONTENT_ITEM_TYPE,
14835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoneNumber(context, dbHelper, contactAggregator));
14845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Nickname.CONTENT_ITEM_TYPE,
14855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNickname(context, dbHelper, contactAggregator));
14865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredName.CONTENT_ITEM_TYPE,
14875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredName(context, dbHelper, contactAggregator,
1488bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
14895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredPostal.CONTENT_ITEM_TYPE,
14905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredPostal(context, dbHelper, contactAggregator,
1491bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
14925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(GroupMembership.CONTENT_ITEM_TYPE,
14935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForGroupMembership(context, dbHelper, contactAggregator,
1494bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
14955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Photo.CONTENT_ITEM_TYPE,
14965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoto(context, dbHelper, contactAggregator, photoStore));
14975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Note.CONTENT_ITEM_TYPE,
14985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNote(context, dbHelper, contactAggregator));
1499bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1500bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1501193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    @VisibleForTesting
1502193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1503bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1504bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1505bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1506bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1507bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1508bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1509bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1510bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1511bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1512bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1513bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1514bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1515bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
151615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
151715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
151815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
151915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
152015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
152115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
152215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
152315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1524bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
152515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
152615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1527bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1528bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1529bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1530bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1531bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
153215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
153315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
153415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
153515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
153615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
153715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
15385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                // Update the accounts for both the contacts and profile DBs.
153915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
15405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToContactMode();
1541bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
15425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToProfileMode();
15435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                accountsChanged |= updateAccountsInBackground(accounts);
15445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1545bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1546bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1547bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1548bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1549bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1550bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1551bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1552bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1553bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1554bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1555fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1556fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1557fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1558fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1559fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1560bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1561bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1562bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1563bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1564bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1565bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1566bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
156705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
156805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
156905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
157005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
157105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1572bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1573bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1574bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1575bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1576bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1577bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1578bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1579bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1580bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1581bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1582bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1583f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1584f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case BACKGROUND_TASK_CLEANUP_PHOTOS: {
1585f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check rate limit.
1586f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long now = System.currentTimeMillis();
1587f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (now - mLastPhotoCleanup > PHOTO_CLEANUP_RATE_LIMIT) {
1588f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    mLastPhotoCleanup = now;
15895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
15905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // Clean up photo stores for both contacts and profiles.
15915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToContactMode();
15925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    cleanupPhotoStore();
15935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToProfileMode();
1594f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    cleanupPhotoStore();
1595f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    break;
1596f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
1597f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1598bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
15994cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
16004cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
160153fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
16023826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
16033826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
16044f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
16054f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
16064f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1607fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
16084cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
160951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
161051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
161151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
161251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
161351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
161451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
161551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
161651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1617bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1618f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1619f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1620f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1621f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1622f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1623f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
162451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
162551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
162651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
162751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
162851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
162951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
163051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
163151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
163251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
16335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, currentLocale);
16345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.setLocale(this, currentLocale);
1635bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
163635997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        invalidateFastScrollingIndexCache();
1637bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1638bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
163951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1640fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1641fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1642fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1643fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1644fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1645fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1646fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1647fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
16485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mContactsHelper.getWritableDatabase();
16495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase profileDb = mProfileHelper.getWritableDatabase();
1650fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
16515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileDb.beginTransaction();
1652fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1653fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1654fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
16555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.setTransactionSuccessful();
1656fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1657fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
16585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.endTransaction();
1659fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1660fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1661fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1662fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1663fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
166405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
166505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
166605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
166705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1668bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1669bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
167051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
167151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
16723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
16733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
16743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
16753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
16763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
16773826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
16783e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        // No accounts/no contacts status is true if there are no account and
16795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // there are no contacts or one profile contact
16803e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        if (mContactsAccountCount == 0) {
16815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long contactsNum = DatabaseUtils.queryNumEntries(mContactsHelper.getReadableDatabase(),
16823e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    Tables.CONTACTS, null);
16835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long profileNum = DatabaseUtils.queryNumEntries(mProfileHelper.getReadableDatabase(),
16845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Tables.CONTACTS, null);
16855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
16865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // TODO: Different status if there is a profile but no contacts?
16875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (contactsNum == 0 && profileNum <= 1) {
16883e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
16893e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            } else {
16903e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NORMAL);
16913e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            }
16923826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
16933826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
16943826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
16953826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
16963826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
1697193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    @VisibleForTesting
1698f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    protected void cleanupPhotoStore() {
16995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
1700d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        mActiveDb.set(db);
17016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
17026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Assemble the set of photo store file IDs that are in use, and send those to the photo
1703f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // store.  Any photos that aren't in that set will be deleted, and any photos that no
1704f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // longer exist in the photo store will be returned for us to clear out in the DB.
17057cf50494501938f175d288077145acf49da8f171Daniel Lehmann        long photoMimeTypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
17066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Cursor c = db.query(Views.DATA, new String[]{Data._ID, Photo.PHOTO_FILE_ID},
17077cf50494501938f175d288077145acf49da8f171Daniel Lehmann                DataColumns.MIMETYPE_ID + "=" + photoMimeTypeId + " AND "
1708f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + Photo.PHOTO_FILE_ID + " IS NOT NULL", null, null, null, null);
17096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> usedPhotoFileIds = Sets.newHashSet();
17106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToDataId = Maps.newHashMap();
1711f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
1712f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            while (c.moveToNext()) {
17136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long dataId = c.getLong(0);
17146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(1);
17156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToDataId.put(photoFileId, dataId);
17176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
17186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } finally {
17196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            c.close();
17206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
17216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
17226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Also query for all social stream item photos.
1723c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        c = db.query(Tables.STREAM_ITEM_PHOTOS + " JOIN " + Tables.STREAM_ITEMS
1724887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki                + " ON " + StreamItemPhotos.STREAM_ITEM_ID + "=" + StreamItemsColumns.CONCRETE_ID,
17256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                new String[]{
1726c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_ID,
1727c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID,
1728887a804b03fcb37be1fde000945dcfd799b3c012Makoto Onuki                        StreamItemPhotos.PHOTO_FILE_ID
17296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                },
17306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                null, null, null, null, null);
17316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
17326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
17336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
17346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            while (c.moveToNext()) {
17356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemPhotoId = c.getLong(0);
17366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemId = c.getLong(1);
17376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(2);
17386802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17396802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
17406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
1741f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1742f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } finally {
1743f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            c.close();
1744f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1745f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1746f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Run the photo store cleanup.
17475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> missingPhotoIds = mPhotoStore.get().cleanup(usedPhotoFileIds);
1748f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1749d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        // If any of the keys we're using no longer exist, clean them up.  We need to do these
1750d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        // using internal APIs or direct DB access to avoid permission errors.
17516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!missingPhotoIds.isEmpty()) {
1752f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
1753d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.beginTransactionWithListener(this);
1754d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                for (long missingPhotoId : missingPhotoIds) {
1755d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    if (photoFileIdToDataId.containsKey(missingPhotoId)) {
1756d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        long dataId = photoFileIdToDataId.get(missingPhotoId);
1757d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        ContentValues updateValues = new ContentValues();
1758d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        updateValues.putNull(Photo.PHOTO_FILE_ID);
1759d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        updateData(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
1760d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                updateValues, null, null, false);
1761d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    }
1762d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
1763d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        // For missing photos that were in stream item photos, just delete the
1764d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        // stream item photo.
1765d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        long streamItemPhotoId = photoFileIdToStreamItemPhotoId.get(missingPhotoId);
1766d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        db.delete(Tables.STREAM_ITEM_PHOTOS, StreamItemPhotos._ID + "=?",
1767d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                new String[]{String.valueOf(streamItemPhotoId)});
1768d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    }
1769d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                }
1770d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.setTransactionSuccessful();
1771d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } catch (Exception e) {
1772d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                // Cleanup failure is not a fatal problem.  We'll try again later.
1773d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                Log.e(TAG, "Failed to clean up outdated photo references", e);
1774d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } finally {
1775d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.endTransaction();
1776f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1777f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1778f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1779f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
17806efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    @Override
1781b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1782b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
178331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
178431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
17856efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    @Override
17866efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected ThreadLocal<ContactsTransaction> getTransactionHolder() {
17876efb7db26598b105342d02207e0ca1c8725c10daDave Santoro        return mTransactionHolder;
17886efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    }
17896efb7db26598b105342d02207e0ca1c8725c10daDave Santoro
17905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public ProfileProvider getProfileProvider() {
17915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return new ProfileProvider(this);
17925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
17935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1794524913c66ce75ca8dec127ac88e3bc2249c246d9Dave Santoro    @VisibleForTesting
1795f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* package */ PhotoStore getPhotoStore() {
17965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mContactsPhotoStore;
1797f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1798f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1799d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    @VisibleForTesting
1800d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    /* package */ PhotoStore getProfilePhotoStore() {
1801d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        return mProfilePhotoStore;
1802d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    }
1803d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro
1804c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki    /**
1805c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki     * Maximum dimension (height or width) of photo thumbnails.
1806c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki     */
1807c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki    public int getMaxThumbnailDim() {
1808c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki        return PhotoProcessor.getMaxThumbnailSize();
180987614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
181087614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1811c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki    /**
1812c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki     * Maximum dimension (height or width) of display photos.  Larger images will be scaled
1813c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki     * to fit.
1814c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki     */
1815c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki    public int getMaxDisplayPhotoDim() {
1816c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki        return PhotoProcessor.getMaxDisplayPhotoSize();
181787614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
181887614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1819013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1820013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1821013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1822013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
18235df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
18245df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
18255df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
18265df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
1827193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    @VisibleForTesting
1828ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
182972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
183072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
183172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1832193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    @VisibleForTesting
18335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
18345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
18355dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
18365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
18375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean inProfileMode() {
18385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Boolean profileMode = mInProfileMode.get();
18395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return profileMode != null && profileMode;
18405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1842a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1843a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1844a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
184510178e5e0b9de566e04508b624a89860c61787d6Makoto Onuki    @NeededForTesting
184610178e5e0b9de566e04508b624a89860c61787d6Makoto Onuki    void wipeData() {
184735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        invalidateFastScrollingIndexCache();
18485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.wipeData();
18495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.wipeData();
18505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore.clear();
18515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore.clear();
18523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1853a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1854a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1855568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
185615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1857568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1858568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1859568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1860568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1861568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
186215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
186315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
186415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
186515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
186615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
186715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
186815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
186915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
187015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
187115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
187215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1873ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1874568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1875568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1876568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
18775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
18785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI should be directed to the profile
18795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database rather than the contacts database.  This is true under either
18805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * of three conditions:
18815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 1. The URI itself is specifically for the profile.
18825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 2. The URI contains ID references that are in the profile ID-space.
18835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 3. The URI contains lookup key references that match the special profile lookup key.
18845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
18855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB operation to the profile database.
18865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
18875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDb(Uri uri) {
18885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return sUriMatcher.mapsToProfile(uri);
18895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
18915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
18925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI with the given values being inserted
18935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * should be directed to the profile database rather than the contacts
18945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database.  This is true if the URI already maps to the profile DB from
18955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a call to {@link #mapsToProfileDb} or if the URI matches a URI that
18965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * specifies parent IDs via the ContentValues, and the given ContentValues
18975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * contains an ID in the profile ID-space.
18985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
18995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param values The values being inserted.
19005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB insert to the profile database.
19015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
19025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDbWithInsertedValues(Uri uri, ContentValues values) {
19035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
19045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return true;
19055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
19065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int match = sUriMatcher.match(uri);
19075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (INSERT_URI_ID_VALUE_MAP.containsKey(match)) {
19085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String idField = INSERT_URI_ID_VALUE_MAP.get(match);
19095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (values.containsKey(idField)) {
19105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = values.getAsLong(idField);
19115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
19125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return true;
19135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
19145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
19155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
19165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return false;
19175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
19185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
19195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
19205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
19215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a profile operation.
19225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
19236efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected void switchToProfileMode() {
19245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mProfileHelper);
19255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mProfileTransactionContext);
19265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mProfileAggregator);
19275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mProfilePhotoStore);
19285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(true);
19295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
19305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
19315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
19325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
19335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a contacts operation.
19345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
19356efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected void switchToContactMode() {
19365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
19375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mContactTransactionContext);
19385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mContactAggregator);
19395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mContactsPhotoStore);
19405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(false);
19415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
194236aa19fbab3722288b7f0917166ef6990ab7b52cDave Santoro        // Clear out the active database; modification operations will set this to the contacts DB.
194336aa19fbab3722288b7f0917166ef6990ab7b52cDave Santoro        mActiveDb.set(null);
19445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
19455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1946568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1947568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
194815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
194936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
195036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
195136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
195236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
19535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDbWithInsertedValues(uri, values)) {
1954078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
1955078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.insert(uri, values);
19565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
19575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
19585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.insert(uri, values);
19595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
1960568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1961568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1962568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1963568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
196415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
196510178e5e0b9de566e04508b624a89860c61787d6Makoto Onuki            // Update on PROVIDER_STATUS used to be used as a trigger to re-start legacy contact
196610178e5e0b9de566e04508b624a89860c61787d6Makoto Onuki            // import.  Now that we no longer support it, we just ignore it.
1967bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
1968bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
196910178e5e0b9de566e04508b624a89860c61787d6Makoto Onuki                return 0;
1970bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
1971bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
197215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
197336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
197436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
197536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
197636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
19775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
1978078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
1979078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.update(uri, values, selection, selectionArgs);
19805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
19815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
19825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.update(uri, values, selection, selectionArgs);
19835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
1984568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1985568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1986568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1987568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
198815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
198936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
199036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
199136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
199236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
19935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
1994078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
1995078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.delete(uri, selection, selectionArgs);
19965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
19975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
19985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.delete(uri, selection, selectionArgs);
19995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Replaces the current (thread-local) database to use for the operation with the given one.
20045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param db The database to use.
20055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /* package */ void substituteDb(SQLiteDatabase db) {
20075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
2008568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2009568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2010568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
201182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    public Bundle call(String method, String arg, Bundle extras) {
201282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        waitForAccess(mReadAccessLatch);
201382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (method.equals(Authorization.AUTHORIZATION_METHOD)) {
201482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Uri uri = (Uri) extras.getParcelable(Authorization.KEY_URI_TO_AUTHORIZE);
201582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
201682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Check permissions on the caller.  The URI can only be pre-authorized if the caller
201782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // already has the necessary permissions.
201882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            enforceSocialStreamReadPermission(uri);
201982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            if (mapsToProfileDb(uri)) {
202082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                mProfileProvider.enforceReadPermission(uri);
202182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
202282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
202382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // If there hasn't been a security violation yet, we're clear to pre-authorize the URI.
202482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Uri authUri = preAuthorizeUri(uri);
202582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Bundle response = new Bundle();
202682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            response.putParcelable(Authorization.KEY_AUTHORIZED_URI, authUri);
202782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            return response;
202882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        }
202982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return null;
203082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
203182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
203282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
203382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Pre-authorizes the given URI, adding an expiring permission token to it and placing that
203482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * in our map of pre-authorized URIs.
203582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @param uri The URI to pre-authorize.
203682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @return A pre-authorized URI that will not require special permissions to use.
203782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
203882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private Uri preAuthorizeUri(Uri uri) {
203982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        String token = String.valueOf(mRandom.nextLong());
204082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        Uri authUri = uri.buildUpon()
204182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                .appendQueryParameter(PREAUTHORIZED_URI_TOKEN, token)
204282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                .build();
204382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        long expiration = SystemClock.elapsedRealtime() + mPreAuthorizedUriDuration;
204482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        mPreAuthorizedUris.put(authUri, expiration);
204582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
204682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return authUri;
204782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
204882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
204982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
205082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Checks whether the given URI has an unexpired permission token that would grant access to
205182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * query the content.  If it does, the regular permission check should be skipped.
205282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @param uri The URI being accessed.
205382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @return Whether the URI is a pre-authorized URI that is still valid.
205482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
205582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    public boolean isValidPreAuthorizedUri(Uri uri) {
205682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        // Only proceed if the URI has a permission token parameter.
205782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (uri.getQueryParameter(PREAUTHORIZED_URI_TOKEN) != null) {
205882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // First expire any pre-authorization URIs that are no longer valid.
205982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            long now = SystemClock.elapsedRealtime();
206082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Set<Uri> expiredUris = Sets.newHashSet();
206182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            for (Uri preAuthUri : mPreAuthorizedUris.keySet()) {
206282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                if (mPreAuthorizedUris.get(preAuthUri) < now) {
206382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                    expiredUris.add(preAuthUri);
206482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                }
206582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
206682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            for (Uri expiredUri : expiredUris) {
206782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                mPreAuthorizedUris.remove(expiredUri);
206882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
206982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
207082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Now check to see if the pre-authorized URI map contains the URI.
207182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            if (mPreAuthorizedUris.containsKey(uri)) {
207282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                // Unexpired token - skip the permission check.
207382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                return true;
207482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
207582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        }
207682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return false;
207782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
207882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
207982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    @Override
2080078f588cef389358adabc579de00747878f3c108Dave Santoro    protected boolean yield(ContactsTransaction transaction) {
2081078f588cef389358adabc579de00747878f3c108Dave Santoro        // If there's a profile transaction in progress, and we're yielding, we need to
2082078f588cef389358adabc579de00747878f3c108Dave Santoro        // end it.  Unlike the Contacts DB yield (which re-starts a transaction at its
2083078f588cef389358adabc579de00747878f3c108Dave Santoro        // conclusion), we can just go back into a state in which we have no active
2084078f588cef389358adabc579de00747878f3c108Dave Santoro        // profile transaction, and let it be re-created as needed.  We can't hold onto
2085078f588cef389358adabc579de00747878f3c108Dave Santoro        // the transaction without risking a deadlock.
2086078f588cef389358adabc579de00747878f3c108Dave Santoro        SQLiteDatabase profileDb = transaction.removeDbForTag(PROFILE_DB_TAG);
2087078f588cef389358adabc579de00747878f3c108Dave Santoro        if (profileDb != null) {
2088078f588cef389358adabc579de00747878f3c108Dave Santoro            profileDb.setTransactionSuccessful();
2089078f588cef389358adabc579de00747878f3c108Dave Santoro            profileDb.endTransaction();
2090078f588cef389358adabc579de00747878f3c108Dave Santoro        }
2091078f588cef389358adabc579de00747878f3c108Dave Santoro
2092078f588cef389358adabc579de00747878f3c108Dave Santoro        // Now proceed with the Contacts DB yield.
2093078f588cef389358adabc579de00747878f3c108Dave Santoro        SQLiteDatabase contactsDb = transaction.getDbForTag(CONTACTS_DB_TAG);
2094078f588cef389358adabc579de00747878f3c108Dave Santoro        return contactsDb != null && contactsDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY);
2095078f588cef389358adabc579de00747878f3c108Dave Santoro    }
2096078f588cef389358adabc579de00747878f3c108Dave Santoro
2097078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
2098568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2099568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
210015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
2101078f588cef389358adabc579de00747878f3c108Dave Santoro        return super.applyBatch(operations);
2102568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2103568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
21044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
21057b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
21067b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
2107078f588cef389358adabc579de00747878f3c108Dave Santoro        return super.bulkInsert(uri, values);
21087b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
21097b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
21107b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
2111078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onBegin() {
2112bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2113fb25f3a5a86ca15de8507baf02a357a63032af62Makoto Onuki            Log.v(TAG, "onBeginTransaction: " + (inProfileMode() ? "profile" : "contacts"));
2114b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
21155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
21165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileAggregator.clearPendingAggregations();
21175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileTransactionContext.clear();
21185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.clearPendingAggregations();
21205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactTransactionContext.clear();
21215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2122b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2123b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2124285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2125078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onCommit() {
2126bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2127fb25f3a5a86ca15de8507baf02a357a63032af62Makoto Onuki            Log.v(TAG, "beforeTransactionCommit: " + (inProfileMode() ? "profile" : "contacts"));
2128b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2129b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
21305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateInTransaction(mTransactionContext.get(), mActiveDb.get());
21311a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
21321a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
21335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateAllVisible();
213435997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
213535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            // Need to rebuild the fast-indxer bundle.
213635997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            invalidateFastScrollingIndexCache();
21371a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
21383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2139bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
2140bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
21413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
21423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
21433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
21443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
2145b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2146b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2147078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
2148078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onRollback() {
2149fb25f3a5a86ca15de8507baf02a357a63032af62Makoto Onuki        if (VERBOSE_LOGGING) {
2150fb25f3a5a86ca15de8507baf02a357a63032af62Makoto Onuki            Log.v(TAG, "beforeTransactionRollback: " + (inProfileMode() ? "profile" : "contacts"));
2151fb25f3a5a86ca15de8507baf02a357a63032af62Makoto Onuki        }
2152fb25f3a5a86ca15de8507baf02a357a63032af62Makoto Onuki        // mDbHelper may not be pointing at the "right" db helper due to a bug,
2153fb25f3a5a86ca15de8507baf02a357a63032af62Makoto Onuki        // so we invalidate both for now.
2154fb25f3a5a86ca15de8507baf02a357a63032af62Makoto Onuki        mContactsHelper.invalidateAllCache();
2155fb25f3a5a86ca15de8507baf02a357a63032af62Makoto Onuki        mProfileHelper.invalidateAllCache();
2156078f588cef389358adabc579de00747878f3c108Dave Santoro    }
2157078f588cef389358adabc579de00747878f3c108Dave Santoro
2158bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
21595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleContacts = mTransactionContext.get().getStaleSearchIndexContactIds();
21605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleRawContacts = mTransactionContext.get().getStaleSearchIndexRawContactIds();
2161bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
2162bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
21635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().clearSearchIndexUpdates();
2164bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
2165bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
2166bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
2167b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2168bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2169b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2170b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
21711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
21725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (long rawContactId : mTransactionContext.get().getInsertedRawContactIds()) {
21735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateRawContactDisplayName(mActiveDb.get(), rawContactId);
21745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().onRawContactInsert(mTransactionContext.get(), mActiveDb.get(),
21755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    rawContactId);
217624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
217724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
21785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> dirtyRawContacts = mTransactionContext.get().getDirtyRawContactIds();
2179d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
2180a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2181a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2182d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
2183a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
21845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2185a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2186a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
21875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> updatedRawContacts = mTransactionContext.get().getUpdatedRawContactIds();
2188d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
2189a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2190a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2191d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
2192a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
21935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2194b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2195b5a4add17815167d20a90645779df34cdf45280dFred Quintana
21965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Update sync states.
21975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (Map.Entry<Long, Object> entry : mTransactionContext.get().getUpdatedSyncStates()) {
2198b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
21995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (mDbHelper.get().getSyncState().update(mActiveDb.get(), id, entry.getValue()) <= 0) {
22009d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
22019d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
22029d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2203b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2204b5a4add17815167d20a90645779df34cdf45280dFred Quintana
22055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().clear();
2206b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2207b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2208a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2209a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2210a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2211a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2212d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
2213b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2214a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2215b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2216a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2217a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2218285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2219285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2220285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2221cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
222281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
222381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
222481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
222581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
222681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
222781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
222881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2229cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2230568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
223151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
22323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
22333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
22343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
22353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
223651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
223751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2238f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
22395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
22405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return getDataRowHandlerForProfile(mimeType);
22415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
22423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
22433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
22446d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
22455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mContactsHelper, mContactAggregator, mimeType);
22463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
22473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
22483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
22493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
22503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
22515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public DataRowHandler getDataRowHandlerForProfile(final String mimeType) {
22525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        DataRowHandler handler = mProfileDataRowHandlers.get(mimeType);
22535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (handler == null) {
22545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            handler = new DataRowHandlerForCustomMimetype(
22555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mProfileHelper, mProfileAggregator, mimeType);
22565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileDataRowHandlers.put(mimeType, handler);
22575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
22585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return handler;
22595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
22605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
22614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2262de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2263bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
22641129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2265b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2266f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
22675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
22685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
2269078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
22705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
22715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2272f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2273f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2274f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2275a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2276a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
227735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2278a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
227935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
22805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
22815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mDbHelper.get().getSyncState().insert(mActiveDb.get(), values);
228235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
228335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2284d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
228535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
2286d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
22876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
22886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
22896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
229024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
229124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
229224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
229324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
229424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2295d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
2296d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
229735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
22985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter);
2299f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2300a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2301a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2302a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2303193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki            case RAW_CONTACTS_ID_DATA:
2304d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
230535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
2306193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki                int segment = match == RAW_CONTACTS_ID_DATA ? 1 : 2;
2307d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(segment));
2308f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2309f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2310a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2311a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2312a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
23133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
23143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
23153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
23163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
23173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
23183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
23193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
23200c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
23210c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
232235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
2323f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2324f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2325a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2326a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2327a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2328ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2329f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2330f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2331ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2332ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2333ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2334eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
23355aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
233643880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2337eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2338eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2339eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
23405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
23415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
234282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
23431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
23441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
23451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
23463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
23473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
23483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
23493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
23503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
23513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
23523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
23533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
23543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
23553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
23563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
23573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
23583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
23593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
23603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
23613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
23623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
23633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
23643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2365a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
236681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2367f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2368a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2369a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
23707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
23717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
23727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
23737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2374de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2375a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2376a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2377a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2378e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2379e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2380e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2381e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2382e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2383e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2384e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2385e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2386e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2387e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2388e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2389e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2390e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
23917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2392e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2393f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2394f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2395e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2396f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2397f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2398f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2399e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2400e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2401e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2402e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2403e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
24045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2405fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2406e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2407e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2408e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2409e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2410e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2411e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2412e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2413e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2414e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2415e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2416e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2417e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
24185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2419fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2420e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2421e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2422e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2423f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2424f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2425e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2426f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2427f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2428e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2429e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2430f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2431f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2432e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2433f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2434f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2435f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2436f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2437035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2438f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2439e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
24407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
24417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
244343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Resolves the account and builds an {@link AccountWithDataSet} based on the data set specified
244443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * in the URI or values (if any).
244543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param uri Current {@link Uri} being operated on.
244643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param values {@link ContentValues} to read and possibly update.
244743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     */
244843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private AccountWithDataSet resolveAccountWithDataSet(Uri uri, ContentValues values) {
24493593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final Account account = resolveAccount(uri, values);
245043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = null;
245143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (account != null) {
245243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
245343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (dataSet == null) {
24543593682b8d9213fde576a0cff54458ad50563980Dave Santoro                dataSet = values.getAsString(RawContacts.DATA_SET);
2455a71dc460ca951c7aca591f3f470c160cde70a1e3Dave Santoro            } else {
24563593682b8d9213fde576a0cff54458ad50563980Dave Santoro                values.put(RawContacts.DATA_SET, dataSet);
245743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
24589ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            accountWithDataSet = AccountWithDataSet.get(account.name, account.type, dataSet);
245943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        }
246043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountWithDataSet;
246143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    }
246243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
246343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    /**
24649d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     * Same as {@link #resolveAccountWithDataSet}, but returns the account id for the
24659d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     *     {@link AccountWithDataSet}.  Used for insert.
24669d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     *
24679d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     * May update the account cache; must be used only in a transaction.
24689d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki     */
24699d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    private long resolveAccountIdInTransaction(Uri uri, ContentValues values) {
24709d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        return mDbHelper.get().getOrCreateAccountIdInTransaction(
24719d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                resolveAccountWithDataSet(uri, mValues));
24729d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    }
24739d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
24749d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    /**
2475d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
24766bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
24776bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
24786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
24796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2480d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2481de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
24826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
24836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
24846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
248524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2486a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2487f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2488f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2489dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
2490a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2491a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
24925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2493f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2494f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2495f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2496f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
24979d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final long accountId = resolveAccountIdInTransaction(uri, mValues);
24989d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        mValues.remove(RawContacts.ACCOUNT_NAME);
24999d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        mValues.remove(RawContacts.ACCOUNT_TYPE);
25009d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        mValues.remove(RawContacts.DATA_SET);
25019d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        mValues.put(RawContactsColumns.ACCOUNT_ID, accountId);
25027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25033d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
25043d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2505f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
25063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
25073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
25085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long rawContactId = mActiveDb.get().insert(Tables.RAW_CONTACTS,
25095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                RawContacts.CONTACT_ID, mValues);
2510f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
25115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2512f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2513f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
25145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markNewForAggregation(rawContactId, aggregationMode);
2515285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
25165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Trigger creation of a Contact based on this RawContact at the end of transaction
25179d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        mTransactionContext.get().rawContactInserted(rawContactId, accountId);
2518f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2519dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2520dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2521dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2522dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2523dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2524dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2525dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2526dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
25273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2528023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2529a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2530a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2531dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2532dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2533dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2534dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2535dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2536dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2537dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2538dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2539dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
25405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.GROUPS + "," + Tables.RAW_CONTACTS,
25415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROJECTION_GROUP_ID, selection,
2542dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2543dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2544dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2545dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2546dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2547dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2548dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2549dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2550dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2551dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2552dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2553dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2554dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2555dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2556dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2557dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2558dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2559dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2560dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2561dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2562dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2563dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2564dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2565dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2566dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2567dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2568dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2569dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2570dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
25715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
25725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().insert(Tables.DATA, null, groupMembershipValues);
2573dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2574dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2575dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2576dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
25775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Long.toString(mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2578dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2579dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
25805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2581dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2582dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2583a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2584a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2585a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2586a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2587a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2588a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2589f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2590a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2591de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2592de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
259367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2594de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
259520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2596de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2597de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2598de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
25995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
2600de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2601de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2602508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2603de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2604de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2605de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2606de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2607de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
26084097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
26095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.get().getMimeTypeId(mimeType));
2610de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2611a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2612a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
26135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = rowHandler.insert(mActiveDb.get(), mTransactionContext.get(), rawContactId, mValues);
2614f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
26155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().markRawContactDirty(rawContactId);
2616a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
26175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactUpdated(rawContactId);
2618a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
26194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
26204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
26213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
26233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
26243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
26253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
26263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
26273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
26283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
26293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
26303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
26313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
26333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
26343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
26353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
26363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
26383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26396802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to insert accounts params - they don't exist in the stream items table.
26406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_NAME);
26416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_TYPE);
26426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
26433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
26445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = mActiveDb.get().insert(Tables.STREAM_ITEMS, null, mValues);
26456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (id == -1) {
26466802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Insertion failed.
26476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return 0;
26486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
26493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
26513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
26523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
26533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
26543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
26563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
26603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
26613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
26623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
26633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
26646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return the stream item photo _ID of the newly created row, or 0 if there was an issue
26656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     with processing the photo or creating the row
26663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
26673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
26683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
26693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
26703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
26713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
26733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
26743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
26753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Don't attempt to insert accounts params - they don't exist in the stream item
26776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // photos table.
26786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_NAME);
26796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_TYPE);
26803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Process the photo and store it.
26826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (processStreamItemPhoto(mValues, false)) {
26836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Insert the stream item photo.
26845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mActiveDb.get().insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
26856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
26863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
26873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
26883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
26893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
26903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
26916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Processes the photo contained in the {@link ContactsContract.StreamItemPhotos#PHOTO}
26926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * field of the given values, attempting to store it in the photo store.  If successful,
26936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * the resulting photo file ID will be added to the values for insert/update in the table.
26946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * <p>
26956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * If updating, it is valid for the picture to be empty or unspecified (the function will
26966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * still return true).  If inserting, a valid picture must be specified.
26976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param values The content values provided by the caller.
26986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param forUpdate Whether this photo is being processed for update (vs. insert).
26996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return Whether the insert or update should proceed.
27006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
27016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    private boolean processStreamItemPhoto(ContentValues values, boolean forUpdate) {
27026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!values.containsKey(StreamItemPhotos.PHOTO)) {
27036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
27046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
27056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PHOTO);
27066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (photoBytes == null) {
27076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
27086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
27096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
27106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo and store it.
27116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
27125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long photoFileId = mPhotoStore.get().insert(new PhotoProcessor(photoBytes,
2713c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki                    getMaxDisplayPhotoDim(), getMaxThumbnailDim(), true), true);
27146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (photoFileId != 0) {
27156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.put(StreamItemPhotos.PHOTO_FILE_ID, photoFileId);
27166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.remove(StreamItemPhotos.PHOTO);
27176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return true;
27186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            } else {
27196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Couldn't store the photo, return 0.
27206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Log.e(TAG, "Could not process stream item photo for insert");
27216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return false;
27226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
27236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } catch (IOException ioe) {
27246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            Log.e(TAG, "Could not process stream item photo for insert", ioe);
27256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return false;
27266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
27276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
27286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
27296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
27303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
27313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
27323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
27333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
27353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
27365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
27375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItems.RAW_CONTACT_ID},
27383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
27393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
27403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
27413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
27423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
27433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
27443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
27453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
27463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
27473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
27483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
275136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * If the given URI is reading stream items or stream photos, this will run a permission check
275236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * for the android.permission.READ_SOCIAL_STREAM permission - otherwise it will do nothing.
275336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * @param uri The URI to check.
275436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     */
275536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private void enforceSocialStreamReadPermission(Uri uri) {
275682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (SOCIAL_STREAM_URIS.contains(sUriMatcher.match(uri))
275782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                && !isValidPreAuthorizedUri(uri)) {
275836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            getContext().enforceCallingOrSelfPermission(
275936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    "android.permission.READ_SOCIAL_STREAM", null);
276036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        }
276136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    }
276236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
276336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    /**
276436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * If the given URI is modifying stream items or stream photos, this will run a permission check
276536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * for the android.permission.WRITE_SOCIAL_STREAM permission - otherwise it will do nothing.
276636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * @param uri The URI to check.
276736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     */
276836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private void enforceSocialStreamWritePermission(Uri uri) {
276936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        if (SOCIAL_STREAM_URIS.contains(sUriMatcher.match(uri))) {
277036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            getContext().enforceCallingOrSelfPermission(
277136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    "android.permission.WRITE_SOCIAL_STREAM", null);
277236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        }
277336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    }
277436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
277536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    /**
27763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
27773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
27783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
27793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
27803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
27813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
27823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
27833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
27843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
27853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
27873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
27885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
27893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
27903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
27913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
27923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
27933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
27943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
27953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
27963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
27973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
27983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
27993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
28003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
28013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
28023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
28033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
28043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
28053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
28063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
28073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
28093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
28103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
28123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
28133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
281520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
281620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2817f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
281820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
281920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2820de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2821de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
28220c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Uri dataUri = inProfileMode()
28230c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                ? Uri.withAppendedPath(Profile.CONTENT_URI, RawContacts.Data.CONTENT_DIRECTORY)
28240c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                : Data.CONTENT_URI;
28250c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Cursor c = query(dataUri, DataRowHandler.DataDeleteQuery.COLUMNS,
2826f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
2827de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2828de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
2829f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
2830f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
2831a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
28325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count += rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
2833f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
28345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mTransactionContext.get().markRawContactDirty(rawContactId);
283588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
283620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
283720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2838de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
283920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
284020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
284120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
284220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
284320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
284488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
284588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
284688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
284720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2848f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
284988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
285088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
28514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
2852f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
28534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2854f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
285520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
285620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
285720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
285820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
285920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2860f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
286120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
286220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
286320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
286420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
286520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
286620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
286720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
286820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
286920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
28707a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
287120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
287220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2873a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
28745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
287520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
287620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
287720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
287820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
287920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
288020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2881ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2882ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2883f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2884f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2885f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2886f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
28879d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final long accountId = mDbHelper.get().getOrCreateAccountIdInTransaction(
28889d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                resolveAccountWithDataSet(uri, mValues));
28899d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        mValues.remove(Groups.ACCOUNT_NAME);
28909d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        mValues.remove(Groups.ACCOUNT_TYPE);
28919d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        mValues.remove(Groups.DATA_SET);
28929d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        mValues.put(GroupsColumns.ACCOUNT_ID, accountId);
2893ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2894ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2895f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
289667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
28975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
289867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2899f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2900ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2901dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
2902dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
2903dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
2904dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2905f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2906f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
290773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
290873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
29095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long result = mActiveDb.get().insert(Tables.GROUPS, Groups.TITLE, mValues);
2910ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2911dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
29129d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            // If the inserted group is a favorite group, add all starred raw contacts to it.
29139d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            mSelectionArgs1[0] = Long.toString(accountId);
29145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
2915dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
29169d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    RawContactsColumns.CONCRETE_ACCOUNT_ID + "=?", mSelectionArgs1,
29179d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    null, null, null);
2918892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
2919892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
2920892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
2921892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
2922892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
29235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mTransactionContext.get().markRawContactDirty(rawContactId);
2924892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
2925dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2926892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
2927892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
2928dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2929dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2930dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2931f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
29321a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2933ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2934ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2935ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2936ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2937ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
29385aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2939f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // Before inserting, ensure that no settings record already exists for the
2940f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // values being inserted (this used to be enforced by a primary key, but that no
2941f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // longer works with the nullable data_set field added).
2942f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountName = values.getAsString(Settings.ACCOUNT_NAME);
2943f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountType = values.getAsString(Settings.ACCOUNT_TYPE);
2944f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String dataSet = values.getAsString(Settings.DATA_SET);
2945f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Uri.Builder settingsUri = Settings.CONTENT_URI.buildUpon();
2946f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountName != null) {
2947f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_NAME, accountName);
2948f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
2949f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountType != null) {
2950f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_TYPE, accountType);
2951f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
2952f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (dataSet != null) {
2953f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.DATA_SET, dataSet);
2954f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
295515826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann        Cursor c = queryLocal(settingsUri.build(), null, null, null, null, 0, null);
2956f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        try {
2957f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (c.getCount() > 0) {
29580e21a867a572679d64d79041eb574d13665178d4Dave Santoro                // If a record was found, replace it with the new values.
29590e21a867a572679d64d79041eb574d13665178d4Dave Santoro                String selection = null;
29600e21a867a572679d64d79041eb574d13665178d4Dave Santoro                String[] selectionArgs = null;
29610e21a867a572679d64d79041eb574d13665178d4Dave Santoro                if (accountName != null && accountType != null) {
29620e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    selection = Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=?";
29630e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    if (dataSet == null) {
29640e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selection += " AND " + Settings.DATA_SET + " IS NULL";
29650e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selectionArgs = new String[] {accountName, accountType};
29660e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    } else {
29670e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selection += " AND " + Settings.DATA_SET + "=?";
29680e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selectionArgs = new String[] {accountName, accountType, dataSet};
29690e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    }
29700e21a867a572679d64d79041eb574d13665178d4Dave Santoro                }
29710e21a867a572679d64d79041eb574d13665178d4Dave Santoro                return updateSettings(uri, values, selection, selectionArgs);
2972f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            }
2973f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        } finally {
2974f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            c.close();
2975f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
2976f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro
2977f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // If we didn't find a duplicate, we're fine to insert.
29785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long id = mActiveDb.get().insert(Tables.SETTINGS, null, values);
29795aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
29801a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
29811a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2982e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
29831a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2984e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2985e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2986e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2987ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
298882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
29891f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
299082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
299182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
29920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
29934dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
29944dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
29950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
299682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
29974dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
29984dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
29994dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
30004dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
30011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
30021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3003dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3004dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
300582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
30066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountType = null;
30076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountName = null;
3008f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
30092526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3010dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3011dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3012dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30132526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
30142526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
30151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3016dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3017dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
30190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
30200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
30210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3022dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3023dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3024dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String mimeTypeIdIm = String.valueOf(mDbHelper.get().getMimeTypeIdForIm());
3026dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
30275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String mimeTypeIdEmail = String.valueOf(mDbHelper.get().getMimeTypeIdForEmail());
3028f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3029f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3030f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3031f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3032f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3033f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3034f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
30352526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
30362526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
30372526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
30382526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
30392526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30402526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
30412526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30422526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3043dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
30442526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
30452526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3046dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
30472526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
30482526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3049dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
30502526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
30512526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
30522526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
30532526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30542526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
30552526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3056dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
30572526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
30582526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3059dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3060dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
30611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
306282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
30632526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
30642526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3065dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
306670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
306770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
30681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
30691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
30705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = mActiveDb.get().query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
30712526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
30724394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
30731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
307467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
30755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
30766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountType = cursor.getString(DataContactsQuery.ACCOUNT_TYPE);
30776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountName = cursor.getString(DataContactsQuery.ACCOUNT_NAME);
3078e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
30791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
30801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
30811f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
30821f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
30831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
308431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
308531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
308631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
30871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
30881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
308982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3090a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3091a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3092a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3093a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3094a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3095a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3096a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
309782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3098a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3099a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
310082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
310182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
310282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
310382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
310482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3105a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
310682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
310782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3108aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3109aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
31101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3111a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
31125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().replace(Tables.PRESENCE, null, mValues);
3113a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3114e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
31150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
311682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
311782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
31180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
31190bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Resources resources = getContext().getResources();
31200bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!TextUtils.isEmpty(resPackage)) {
31210bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                PackageManager pm = getContext().getPackageManager();
31220bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                try {
31230bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    resources = pm.getResourcesForApplication(resPackage);
31240bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                } catch (NameNotFoundException e) {
31250bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    Log.w(TAG, "Contact status update resource package not found: "
31260bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            + resPackage);
31270bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                }
31280bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
31290bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer labelResourceId = values.getAsInteger(StatusUpdates.STATUS_LABEL);
31300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
31310bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if ((labelResourceId == null || labelResourceId == 0) && protocol != null) {
31320bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                labelResourceId = Im.getProtocolLabelResource(protocol);
31330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
31340bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String labelResource = getResourceName(resources, "string", labelResourceId);
31350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
31360bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer iconResourceId = values.getAsInteger(StatusUpdates.STATUS_ICON);
31370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
31380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
31390bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String iconResource = getResourceName(resources, "drawable", iconResourceId);
31400bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
3141a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
31425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().deleteStatusUpdate(dataId);
3143a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
31446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
31456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (timestamp != null) {
31465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().replaceStatusUpdate(dataId, timestamp, status, resPackage,
31470bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            iconResourceId, labelResourceId);
31486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                } else {
31495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().insertStatusUpdate(dataId, status, resPackage, iconResourceId,
31500bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            labelResourceId);
31516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
31526802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
31536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // For forward compatibility with the new stream item API, insert this status update
31546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // there as well.  If we already have a stream item from this source, update that
31556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // one instead of inserting a new one (since the semantics of the old status update
31566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // API is to only have a single record).
31576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (rawContactId != -1 && !TextUtils.isEmpty(status)) {
31586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ContentValues streamItemValues = new ContentValues();
31596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3160d5ef5903570e533a501abe6a8e3d533fdb5318fcFlavio Lerda                    // Status updates are text only but stream items are HTML.
3161e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda                    streamItemValues.put(StreamItems.TEXT, statusUpdateToHtml(status));
31626802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.COMMENTS, "");
31636802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_PACKAGE, resPackage);
31646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_ICON, iconResource);
31656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_LABEL, labelResource);
31666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TIMESTAMP,
31676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            timestamp == null ? System.currentTimeMillis() : timestamp);
31686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
31696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Note: The following is basically a workaround for the fact that status
31706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates didn't do any sort of account enforcement, while social stream item
31716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates do.  We can't expect callers of the old API to start passing account
31726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // information along, so we just populate the account params appropriately for
317343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // the raw contact.  Data set is not relevant here, as we only check account
317443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // name and type.
31756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    if (accountName != null && accountType != null) {
31766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName);
31776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType);
31786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
31796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
31806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Check for an existing stream item from this source, and insert or update.
31816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Uri streamUri = StreamItems.CONTENT_URI;
318236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    Cursor c = queryLocal(streamUri, new String[]{StreamItems._ID},
31836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.RAW_CONTACT_ID + "=?",
318436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            new String[]{String.valueOf(rawContactId)},
318515826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann                            null, -1 /* directory ID */, null);
31866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    try {
31876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        if (c.getCount() > 0) {
31886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            c.moveToFirst();
318936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            updateInTransaction(ContentUris.withAppendedId(streamUri, c.getLong(0)),
31906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    streamItemValues, null, null);
31916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        } else {
319236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            insertInTransaction(streamUri, streamItemValues);
31936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        }
31946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    } finally {
31956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        c.close();
31966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
31976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
3198e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3199e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3200bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3201a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
32025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().updateLastStatusUpdateId(contactId);
3203a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3204a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3205a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
32061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
32071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3208e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    /** Converts a status update to HTML. */
3209e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    private String statusUpdateToHtml(String status) {
32104747809486541f7a3d342d3e1dd48fb5ea255ad6Flavio Lerda        return TextUtils.htmlEncode(status);
3211e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    }
3212e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda
32130bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    private String getResourceName(Resources resources, String expectedType, Integer resourceId) {
32140bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        try {
32150bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (resourceId == null || resourceId == 0) return null;
32160bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
32170bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            // Resource has an invalid type (e.g. a string as icon)? ignore
32180bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceEntryName = resources.getResourceEntryName(resourceId);
32190bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceTypeName = resources.getResourceTypeName(resourceId);
32200bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!expectedType.equals(resourceTypeName)) {
32210bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                Log.w(TAG, "Resource " + resourceId + " (" + resourceEntryName + ") is of type " +
32220bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                        resourceTypeName + " but " + expectedType + " is required.");
32230bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                return null;
32240bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
32250bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
32260bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return resourceEntryName;
32270bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        } catch (NotFoundException e) {
32280bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return null;
32290bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        }
32300bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    }
32310bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
32324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3233de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3234bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3235b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3236b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
32375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
32385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
32395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
3240078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
32415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
32425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
3243b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3244f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3245f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3246508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3247508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
324835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
32495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
32505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selection,
32515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
32525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
32535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case SYNCSTATE_ID: {
32545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
32555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
32565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
32575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selectionWithId,
32585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
32595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
326035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
32615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
3262b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3263b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3264b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
32655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().delete(mActiveDb.get(), selectionWithId,
32665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
32675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
3268b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3269cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
327035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
3271cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3272cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3273cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3274cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3275d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
327635997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
3277d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3278dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
32796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
32806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
32819fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
328235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
32832e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
32842e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
32852e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
32865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3287fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
32882e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
32892e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
32905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3291dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
32922e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
32932e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
32949fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
329535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
32969fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
32979fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
32989fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
32999fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
33009fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3301a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
33029fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
33039fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
33049fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
33059fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
33069fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
33079fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
33089fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
33099fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
331060de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
33119fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
33125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = query(mActiveDb.get(), lookupQb, null, selection, args, null, null,
331315826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann                        null, null);
33149fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
33159fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
33169fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3317dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
33189fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
33199fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
33209fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
33219fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
33229fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
33239fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
33249fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
33259fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
33269fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
33279fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
3328b6186821548995dce533ee502e82e9abf4c0aadcMakoto Onuki            case CONTACTS_DELETE_USAGE: {
3329b6186821548995dce533ee502e82e9abf4c0aadcMakoto Onuki                return deleteDataUsage();
3330b6186821548995dce533ee502e82e9abf4c0aadcMakoto Onuki            }
3331b6186821548995dce533ee502e82e9abf4c0aadcMakoto Onuki
3332d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
3333d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
333435997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
33352971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
33365f673b204620c4c241b3b39c6ac0ee063d22f13bMakoto Onuki                Cursor c = mActiveDb.get().query(Views.RAW_CONTACTS,
3337fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
33389d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        appendAccountIdToSelection(uri, selection), selectionArgs,
33399d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        null, null, null);
33402971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
33412971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
33422971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3343fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3344fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3345fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
33462971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
33472971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
33482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
33492971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
33502971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
33512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
33522971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
3353d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
3354d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
335535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
33562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
33575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return deleteRawContact(rawContactId, mDbHelper.get().getContactId(rawContactId),
3358fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3359508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3360508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
33610c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
33620c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
336335997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
3364f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3365944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3366f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
336720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
336820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
336948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
337048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
337148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
3372e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa            case CALLABLES_ID:
3373d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case POSTALS_ID:
3374d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
337535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
3376508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3377f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
33784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
33794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3380ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3381ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3382ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3383f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
33845aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
33852971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
33862971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
33872971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
33882971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
33895f673b204620c4c241b3b39c6ac0ee063d22f13bMakoto Onuki                Cursor c = mActiveDb.get().query(Views.GROUPS, Projections.ID,
33909d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        appendAccountIdToSelection(uri, selection), selectionArgs,
33919d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        null, null, null);
33922971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
33932971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
33945aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
33952971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
33962971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
33972971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
33982971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
339981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3400f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
340181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
34022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3403508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3404508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3405eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
340643880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3407e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3408eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3409eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
34105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
34115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
34120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
34131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
34141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
34153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
34163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
34173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
34183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
34193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
34213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
34223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
34239b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                        StreamItems._ID + "=?",
34243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
34253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
34263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
342782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
342882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
342982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
343082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
343182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                return deleteStreamItems(uri, new ContentValues(),
343282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
343382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
343482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
343582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
343682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
34373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
34383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
34395d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String streamItemId = uri.getPathSegments().get(1);
34405d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String selectionWithId =
34415d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        (StreamItemPhotos.STREAM_ITEM_ID + "=" + streamItemId + " ")
34425d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                                + (selection == null ? "" : " AND (" + selection + ")");
34435d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                return deleteStreamItemPhotos(uri, new ContentValues(),
34445d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        selectionWithId, selectionArgs);
34453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
34463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
34473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
34483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
34493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
34503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
34513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
34523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
34533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
34543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
34553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
34563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
345781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
345881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
34593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
346081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3461508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
34624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
34634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
34641c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3465ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
34665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long groupMembershipMimetypeId = mDbHelper.get()
346794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
34685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
346994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
347094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
347194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
347294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3473f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
34745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
347594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
347694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
347794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3478f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
34795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId,
34805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
348194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
348294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
34831a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
348494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
348594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
348694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
34875aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
34885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().delete(Tables.SETTINGS, selection, selectionArgs);
34891a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3490e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3491e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3492e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3493dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
349496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
34955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
349696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
349796b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3498cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3499cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3500cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3501dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3502cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3503cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3504cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3505cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3506cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
35073826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
35083826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
35095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3510cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3511cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3512fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
35135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
35143826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
35153826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
351682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        // Find and delete stream items associated with the raw contact.
351782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
351882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                new String[]{StreamItems._ID},
351982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
352082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                null, null, null);
352182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        try {
352282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            while (c.moveToNext()) {
352382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                deleteStreamItem(c.getLong(0));
352482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
352582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        } finally {
352682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            c.close();
352782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        }
352882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
3529d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        if (callerIsSyncAdapter || rawContactIsLocal(rawContactId)) {
35305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().delete(Tables.PRESENCE,
35315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
35325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            int count = mActiveDb.get().delete(Tables.RAW_CONTACTS,
35335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    RawContacts._ID + "=" + rawContactId, null);
353441f76a59a31946f6d784dacf9f13d9a4c0bbe203Dave Santoro            mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
3535fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
353633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
35375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().removeContactIfSingleton(rawContactId);
3538dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
353933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
354033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
354133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3542d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    /**
3543d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     * Returns whether the given raw contact ID is local (i.e. has no account associated with it).
3544d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     */
3545d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    private boolean rawContactIsLocal(long rawContactId) {
35469d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS, Projections.LITERAL_ONE,
35479d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                RawContactsColumns.CONCRETE_ID + "=? AND " +
35489d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                RawContactsColumns.ACCOUNT_ID + "=" + Clauses.LOCAL_ACCOUNT_ID,
3549d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {String.valueOf(rawContactId)}, null, null, null);
3550d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        try {
35519d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            return c.getCount() > 0;
3552d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        } finally {
3553d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            c.close();
3554d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        }
3555d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    }
3556d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro
35570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
35589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
35599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
35609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
35619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
35629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
35635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      mActiveDb.get().delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
35649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
35655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      return mActiveDb.get().delete(Tables.PRESENCE, selection, selectionArgs);
35660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
35670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
35683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
35693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
35709d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        int count = 0;
35719d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final Cursor c = mActiveDb.get().query(Views.STREAM_ITEMS, Projections.ID,
35729d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                selection, selectionArgs, null, null, null);
35739d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        try {
35749d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            c.moveToPosition(-1);
35759d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            while (c.moveToNext()) {
35769d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                count += deleteStreamItem(c.getLong(0));
35779d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            }
35789d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        } finally {
35799d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            c.close();
35803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
35819d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        return count;
35823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
35833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
35853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
35865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
35873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
35883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
35893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
35913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
35925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
35933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
35943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
35953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
35963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
35975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS,
35985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                StreamItemPhotos.STREAM_ITEM_ID + "=?",
35993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
36003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
36013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3602dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
360381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
360481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3605cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3606cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3607cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3608cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3609cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3610cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3611dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3612cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3613cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3614b6186821548995dce533ee502e82e9abf4c0aadcMakoto Onuki    private int deleteDataUsage() {
3615a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki        final SQLiteDatabase db = mActiveDb.get();
3616a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki        db.execSQL("UPDATE " + Tables.RAW_CONTACTS + " SET " +
3617a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki                Contacts.TIMES_CONTACTED + "=0," +
3618a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki                Contacts.LAST_TIME_CONTACTED + "=NULL"
3619a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki                );
3620a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki        db.execSQL("UPDATE " + Tables.CONTACTS + " SET " +
3621a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki                Contacts.TIMES_CONTACTED + "=0," +
3622a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki                Contacts.LAST_TIME_CONTACTED + "=NULL"
3623a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki                );
3624a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki        db.delete(Tables.DATA_USAGE_STAT, null, null);
3625a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki
3626a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki        return 1;
3627a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki    }
3628a780048d2caafbd922444b0c08adb81790db4635Makoto Onuki
36294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3630de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3631de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3632bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3633b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3634b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3635b5a4add17815167d20a90645779df34cdf45280dFred Quintana
36365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
36375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
3638078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
36395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
36405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
364135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
364200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
364300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3644b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3645b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
36461129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
36475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().syncStateUpdated(rowId, data);
3648b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3649b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3650b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3651f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3652f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
365300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
365435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
36555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
36565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
3657b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3658b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3659b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3660b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3661b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3662b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3663b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
36645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
36655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionWithId, selectionArgs);
36665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
36675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
36685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
36695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection = appendAccountToSelection(uri, selection);
36705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
36715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
36725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
36735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().update(mActiveDb.get(), values,
3674b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3675b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
367635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3677d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case CONTACTS:
3678d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE: {
367935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
3680dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
368100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
368200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
368300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3684d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
368535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
3686dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3687c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3688c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3689c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
36902e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
36912e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
369235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
36932e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
36942e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
36952e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
36965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3697fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
36982e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
36992e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
37005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3701dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
37022e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
37032e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
37042e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
3705193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki            case RAW_CONTACTS_ID_DATA:
3706d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
370735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
3708193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki                int segment = match == RAW_CONTACTS_ID_DATA ? 1 : 2;
3709d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                final String rawContactId = uri.getPathSegments().get(segment);
37107d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
37117d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
37127d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
37137d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
37147d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
37157d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
37167d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
37177d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
37180c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
37190c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
372035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
3721944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3722f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
372381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3724f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
372581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
372620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
372720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3728c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
372948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
373048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
373148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
3732e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa            case CALLABLES_ID:
373348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
373435997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
3735f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
373681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3737f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
373881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
373900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
374000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
37417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
37425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case RAW_CONTACTS:
37435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_RAW_CONTACTS: {
374435997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
37459d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                selection = appendAccountIdToSelection(uri, selection);
3746dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
37477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
37487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
37497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
37505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
375135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                invalidateFastScrollingIndexCache();
375233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
37534529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
37544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
37554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3756dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3757dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
37584529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
37594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3760dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3761dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
37624529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
37637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
37647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
37657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3766ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
37679d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki               count = updateGroups(uri, values, appendAccountIdToSelection(uri, selection),
3768f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
376981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3770f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
377181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3772ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3773ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3774ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3775ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3776ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
37774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
37784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
377973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
37805aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
37815aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
378281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3783f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
378481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3785ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3786ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3787ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3788127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
37895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count = updateAggregationException(mActiveDb.get(), values);
3790b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3791b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3792b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3793eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3794e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3795e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
379643880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3797eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3798eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3799eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
38005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
38015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
38029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
38039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
38049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
38059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
38063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
38073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
38083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
38093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
38103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
38129b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                count = updateStreamItems(uri, values, StreamItems._ID + "=?",
38133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
38143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
38153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
38163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
381782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
381882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
381982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
382082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                count = updateStreamItems(uri, values,
382182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
382282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
382382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
382482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
382582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
38263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
38273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
38283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
38293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
38303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
38323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
38333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
38343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
38353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
38363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
38373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
38393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
38403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
38413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
38423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
38433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
38443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
38453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
38463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
38473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
384872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
3849bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
385072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
3851d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3852d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3853d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
385446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
385546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
385646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
385746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
385846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
385946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
386046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
386146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
386246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
386381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
386481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3865f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
386681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
386700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
386800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
386900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
38704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
38714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
38729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
38739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
38749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
38759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
38769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
38779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
38789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
38795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.STATUS_UPDATES,
38809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
38819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
38829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
38839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
38849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
38859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
38869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
38879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
38885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.PRESENCE, settableValues,
38899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
38909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
38919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
38929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
38939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
38949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
38959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
38963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
38973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
38983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
38993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
39003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
39016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream items table.
39026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
39036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
39046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
39053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
39065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
39073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
39083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
39093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
39103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
39113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
39123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
39133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
39146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream item
39156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // photos table.
39166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
39176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
39186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
39196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo (since we're updating, it's valid for the photo to not be present).
39206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (processStreamItemPhoto(values, true)) {
39216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // If there's been no exception, the update should be fine.
39225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mActiveDb.get().update(Tables.STREAM_ITEM_PHOTOS, values, selection,
39235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selectionArgs);
39246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
39256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return 0;
39263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
39273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
39289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
39299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
39309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
39319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
39329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
39339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
39349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
39359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
39369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
39379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
39389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
39399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
39409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
39419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
39429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
39439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
39449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
39459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
39469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
39479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
39489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
39499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
39509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
39519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
39529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
39539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
39549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
39559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
39569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
39579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3958aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3959aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
39609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
39619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
39629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
39639d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    private interface GroupAccountQuery {
39649d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        String TABLE = Views.GROUPS;
39659d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
39669d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        String[] COLUMNS = new String[] {
39679d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                Groups._ID,
39689d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                Groups.ACCOUNT_TYPE,
39699d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                Groups.ACCOUNT_NAME,
39709d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                Groups.DATA_SET,
39719d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        };
39729d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        int ID = 0;
39739d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        int ACCOUNT_TYPE = 1;
39749d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        int ACCOUNT_NAME = 2;
39759d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        int DATA_SET = 3;
39769d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    }
397773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
39789d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    private int updateGroups(Uri uri, ContentValues originalValues, String selectionWithId,
39799d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            String[] selectionArgs, boolean callerIsSyncAdapter) {
3980ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3981ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
39829d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final SQLiteDatabase db = mActiveDb.get();
39839d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final ContactsDatabaseHelper dbHelper = mDbHelper.get();
39849d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
39859d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final ContentValues updatedValues = new ContentValues();
39869d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        updatedValues.putAll(originalValues);
39879d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
39889d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        if (!callerIsSyncAdapter && !updatedValues.containsKey(Groups.DIRTY)) {
398973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
399073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
39911a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
39921a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
399394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
399443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
39959d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        // Prepare for account change
39969d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final boolean isAccountNameChanging = updatedValues.containsKey(Groups.ACCOUNT_NAME);
39979d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final boolean isAccountTypeChanging = updatedValues.containsKey(Groups.ACCOUNT_TYPE);
39989d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final boolean isDataSetChanging = updatedValues.containsKey(Groups.DATA_SET);
39999d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final boolean isAccountChanging = isAccountNameChanging || isAccountTypeChanging
40009d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                || isDataSetChanging;
40019d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final String updatedAccountName = updatedValues.getAsString(Groups.ACCOUNT_NAME);
40029d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final String updatedAccountType = updatedValues.getAsString(Groups.ACCOUNT_TYPE);
40039d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final String updatedDataSet = updatedValues.getAsString(Groups.DATA_SET);
40049d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
40059d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        updatedValues.remove(Groups.ACCOUNT_NAME);
40069d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        updatedValues.remove(Groups.ACCOUNT_TYPE);
40079d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        updatedValues.remove(Groups.DATA_SET);
40089d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
40099d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        // We later call requestSync() on all affected accounts.
40109d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final Set<Account> affectedAccounts = Sets.newHashSet();
40119d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
40129d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        // Look for all affected rows, and change them row by row.
40139d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final Cursor c = db.query(GroupAccountQuery.TABLE, GroupAccountQuery.COLUMNS,
40149d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                selectionWithId, selectionArgs, null, null, null);
40159d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        int returnCount = 0;
40169d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        try {
40179d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            c.moveToPosition(-1);
40189d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            while (c.moveToNext()) {
40199d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                final long groupId = c.getLong(GroupAccountQuery.ID);
40209d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
40219d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                mSelectionArgs1[0] = Long.toString(groupId);
40229d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
40239d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                final String accountName = isAccountNameChanging
40249d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        ? updatedAccountName : c.getString(GroupAccountQuery.ACCOUNT_NAME);
40259d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                final String accountType = isAccountTypeChanging
40269d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        ? updatedAccountType : c.getString(GroupAccountQuery.ACCOUNT_TYPE);
40279d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                final String dataSet = isDataSetChanging
40289d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        ? updatedDataSet : c.getString(GroupAccountQuery.DATA_SET);
40299d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
40309d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                if (isAccountChanging) {
40319d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    final long accountId = dbHelper.getOrCreateAccountIdInTransaction(
40329d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                            AccountWithDataSet.get(accountName, accountType, dataSet));
40339d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    updatedValues.put(GroupsColumns.ACCOUNT_ID, accountId);
40349d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                }
40359d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
40369d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // Finally do the actual update.
40379d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                final int count = db.update(Tables.GROUPS, updatedValues,
40389d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        GroupsColumns.CONCRETE_ID + "=?", mSelectionArgs1);
40399d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
40409d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                if ((count > 0)
40419d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        && !TextUtils.isEmpty(accountName)
40429d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        && !TextUtils.isEmpty(accountType)) {
40439d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    affectedAccounts.add(new Account(accountName, accountType));
40449d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                }
40459d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
40469d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                returnCount += count;
40479d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            }
40489d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        } finally {
40499d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            c.close();
40509d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        }
40519d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
405243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // TODO: This will not work for groups that have a data set specified, since the content
405343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // resolver will not be able to request a sync for the right source (unless it is updated
405443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // to key off account with data set).
40559d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        // i.e. requestSync only takes Account, not AccountWithDataSet.
40566ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
40571129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
40589d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            for (Account account : affectedAccounts) {
40599d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                ContentResolver.requestSync(account, ContactsContract.AUTHORITY, new Bundle());
40606ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
40616ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
40629d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        return returnCount;
406394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
406494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
4065b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
4066b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
40675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().update(Tables.SETTINGS, values, selection, selectionArgs);
40681a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
40691a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
4070e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
4071e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
4072e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
4073e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4074dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
4075dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
40764529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
40774529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
40784529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
40794529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
408073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
408197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
408297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
408397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
408497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
408597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
40864529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
40875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
40889d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                Projections.ID, selection,
40894529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
40904529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
40914529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
40924529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
4093dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
40944529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
40954529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
40964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
40974529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
40984529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
40994529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
41004529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
41014529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
41024529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
4103dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
4104dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
41059d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final String selection = RawContactsColumns.CONCRETE_ID + " = ?";
410696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
41079d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
41089d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final ContactsDatabaseHelper dbHelper = mDbHelper.get();
41099d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
411019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
411119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
41129d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
41139d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final boolean isAccountNameChanging = values.containsKey(RawContacts.ACCOUNT_NAME);
41149d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final boolean isAccountTypeChanging = values.containsKey(RawContacts.ACCOUNT_TYPE);
41159d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final boolean isDataSetChanging = values.containsKey(RawContacts.DATA_SET);
41169d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final boolean isAccountChanging = isAccountNameChanging || isAccountTypeChanging
41179d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                || isDataSetChanging;
41189d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
411919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
41209d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        long accountId = 0;
41219d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        String oldAccountType = null;
41229d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        String oldAccountName = null;
41239d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        String oldDataSet = null;
41249d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
41259d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        if (requestUndoDelete || isAccountChanging) {
41265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
41275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selection, mSelectionArgs1, null, null, null);
412819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
412919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
413019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
41319d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    accountId = cursor.getLong(RawContactsQuery.ACCOUNT_ID);
41329d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    oldAccountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
41339d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    oldAccountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
41349d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    oldDataSet = cursor.getString(RawContactsQuery.DATA_SET);
413519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
413619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
413719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
413819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
41399d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            if (isAccountChanging) {
41409d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // We can't change the original ContentValues, as it'll be re-used over all
41419d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // updateRawContact invocations in a transaction, so we need to create a new one.
41429d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // (However we don't want to use mValues here, because mValues may be used in some
41439d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // other methods that are called by this method.)
41449d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                final ContentValues originalValues = values;
41459d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                values = new ContentValues();
41469d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                values.clear();
41479d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                values.putAll(originalValues);
41489d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
41499d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                final AccountWithDataSet newAccountWithDataSet = AccountWithDataSet.get(
41509d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        isAccountNameChanging
41519d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                            ? values.getAsString(RawContacts.ACCOUNT_NAME) : oldAccountName,
41529d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        isAccountTypeChanging
41539d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                            ? values.getAsString(RawContacts.ACCOUNT_TYPE) : oldAccountType,
41549d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        isDataSetChanging
41559d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                            ? values.getAsString(RawContacts.DATA_SET) : oldDataSet
41569d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        );
41579d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                accountId = dbHelper.getOrCreateAccountIdInTransaction(newAccountWithDataSet);
41589d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
41599d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                values.put(RawContactsColumns.ACCOUNT_ID, accountId);
41609d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
41619d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                values.remove(RawContacts.ACCOUNT_NAME);
41629d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                values.remove(RawContacts.ACCOUNT_TYPE);
41639d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                values.remove(RawContacts.DATA_SET);
41649d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            }
41659d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        }
41669d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        if (requestUndoDelete) {
416719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
416819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
416919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
4170f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
41715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
41725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
4173f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
4174f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
4175f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
4176f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
4177f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
4178f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
41795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().markForAggregation(rawContactId, aggregationMode, false);
4180f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
4181f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
4182433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
4183dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
4184dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4185dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
4186dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
41875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateStarred(rawContactId);
4188dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
4189dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
4190dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
4191dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
4192dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
41939d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                if (!callerIsSyncAdapter && isAccountChanging) {
41945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    boolean starred = 0 != DatabaseUtils.longForQuery(mActiveDb.get(),
4195dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
4196dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
4197dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
4198dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4199dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4200dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4201dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
4202dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
42039d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            if (!callerIsSyncAdapter && isAccountChanging) {
4204dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
4205433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
4206dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4207285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
42085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateLookupKeyForRawContact(mActiveDb.get(), rawContactId);
4209285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
4210f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
4211f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
4212f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
4213f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
4214f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
42155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().resetNameVerifiedForOtherRawContacts(rawContactId);
4216f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
42175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateDisplayNameForRawContact(mActiveDb.get(), rawContactId);
4218f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
421919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
42209d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // Note before the accounts refactoring, we used to use the *old* account here,
42219d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // which doesn't make sense, so now we pass the *new* account.
42229d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // (In practice it doesn't matter because there's probably no apps that undo-delete
42239d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // and change accounts at the same time.)
42249d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                mTransactionContext.get().rawContactInserted(rawContactId, accountId);
422519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
42265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
422833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
422933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
4230321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
4231f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
423220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
423320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
423420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
42355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
423620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
423720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
423820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
423920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
424020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
42415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
424220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
424320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
424497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
424597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
424697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
424797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
424897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
4249653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
425020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4251653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
4252653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
42535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = queryLocal(uri,
4254f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                DataRowHandler.DataUpdateQuery.COLUMNS,
425515826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann                selection, selectionArgs, null, -1 /* directory ID */, null);
4256653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
4257653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
4258f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
425920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
4260653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
4261653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
426220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
426320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4264653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
426520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
426620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4267f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
4268653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
4269653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
4270321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
4271653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
4272f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
4273a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
4274f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean updated =
42755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                rowHandler.update(mActiveDb.get(), mTransactionContext.get(), values, c,
42765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        callerIsSyncAdapter);
4277f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
4278f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
4279a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
4280f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return updated ? 1 : 0;
4281321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
4282321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
42838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
4284dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
42858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
42865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.CONTACTS,
42875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[] { Contacts._ID }, selection, selectionArgs, null, null, null);
42888c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
42898c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
42908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
429124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4292dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
42938c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
42948c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
42958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
42968c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
42978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
42988c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
42998c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
43008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
43018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4302dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
4303dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
4304d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
43058c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4306b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
4307d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4308b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
4309d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4310b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
4311d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4312b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
4313d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4314b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
4315d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
4316d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4317d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
43188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
4319d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
4320d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
4321d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
43228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
4323c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
43248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
4325c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
4326c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
43274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
43285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
432997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
43308c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4331dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
43325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
4333dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
4334dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
4335dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
4336dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4337dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4338dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4339dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4340dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4341dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4342dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4343dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4344dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4345dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
43468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
43478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
43488c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4349b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
43508c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4351b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
43528c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4353b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
43548c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4355b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
43568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4357b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
43588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
43598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
43605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int rslt = mActiveDb.get().update(Tables.CONTACTS, mValues, Contacts._ID + "=?",
43615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mSelectionArgs1);
43626e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
43639b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
43649b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
43655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
43665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
43679b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
43689b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4369f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4370d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4371127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4372127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
43730c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
43740c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
437580c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
4376ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
4377ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
43780c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
43790c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
43800c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
43810c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
43820c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
43830c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4384b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4385127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
43860c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
43874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
43884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
43890c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
43904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
43914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
43920c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
43936bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
43946bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
43950c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
43960c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
43970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
43980c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4399127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4400127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
44015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
44025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId1,
440369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
44045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId2,
440569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4406dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
44075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId1);
44085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId2);
4409127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4410127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4411127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4412127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4413b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4414b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
441570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4416bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
44173826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
44183826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
44199ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    private static final String ACCOUNT_STRING_SEPARATOR_OUTER = "\u0001";
44209ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    private static final String ACCOUNT_STRING_SEPARATOR_INNER = "\u0002";
44219d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
44229ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    /** return serialized version of {@code accounts} */
44239ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    @VisibleForTesting
44249ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    static String accountsToString(Set<Account> accounts) {
44259ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final StringBuilder sb = new StringBuilder();
44269ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        for (Account account : accounts) {
44279ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            if (sb.length() > 0) {
44289ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                sb.append(ACCOUNT_STRING_SEPARATOR_OUTER);
44299ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            }
44309ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            sb.append(account.name);
44319ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            sb.append(ACCOUNT_STRING_SEPARATOR_INNER);
44329ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            sb.append(account.type);
44339ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        }
44349ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        return sb.toString();
44359ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    }
44369d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
44379ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    /**
44389ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki     * de-serialize string returned by {@link #accountsToString} and return it.
44399ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki     * If {@code accountsString} is malformed it'll throw {@link IllegalArgumentException}.
44409ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki     */
44419ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    @VisibleForTesting
44429ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    static Set<Account> stringToAccounts(String accountsString) {
44439ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final Set<Account> ret = Sets.newHashSet();
44449ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        if (accountsString.length() == 0) return ret; // no accounts
44459ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        try {
44469ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            for (String accountString : accountsString.split(ACCOUNT_STRING_SEPARATOR_OUTER)) {
44479ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                String[] nameAndType = accountString.split(ACCOUNT_STRING_SEPARATOR_INNER);
44489ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                ret.add(new Account(nameAndType[0], nameAndType[1]));
44499ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            }
44509ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            return ret;
44519ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        } catch (RuntimeException ex) {
44529ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            throw new IllegalArgumentException("Malformed string", ex);
44539ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        }
44549ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    }
44559ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki
44569ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    /**
44579ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki     * @return {@code true} if the given {@code currentSystemAccounts} are different from the
44589ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki     *    accounts we know, which are stored in the {@link DbProperties#KNOWN_ACCOUNTS} property.
44599ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki     */
44609ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    @VisibleForTesting
44619ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    boolean haveAccountsChanged(Account[] currentSystemAccounts) {
44629ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final ContactsDatabaseHelper dbHelper = mDbHelper.get();
44639ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final Set<Account> knownAccountSet;
44649ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        try {
44659ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            knownAccountSet = stringToAccounts(
44669ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                    dbHelper.getProperty(DbProperties.KNOWN_ACCOUNTS, ""));
44679ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        } catch (IllegalArgumentException e) {
44689ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            // Failed to get the last known accounts for an unknown reason.  Let's just
44699ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            // treat as if accounts have changed.
44709ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            return true;
44719ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        }
44729ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final Set<Account> currentAccounts = Sets.newHashSet(currentSystemAccounts);
44739ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        return !knownAccountSet.equals(currentAccounts);
44749ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    }
44759ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki
44769ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    @VisibleForTesting
44779ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    void saveAccounts(Account[] systemAccounts) {
44789ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final ContactsDatabaseHelper dbHelper = mDbHelper.get();
44799ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        dbHelper.setProperty(DbProperties.KNOWN_ACCOUNTS,
44809ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                accountsToString(Sets.newHashSet(systemAccounts)));
44819ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    }
44829ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki
44839ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    private boolean updateAccountsInBackground(Account[] systemAccounts) {
44849ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        if (!haveAccountsChanged(systemAccounts)) {
44859ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            return false;
44869ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        }
4487b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki        if ("1".equals(SystemProperties.get(DEBUG_PROPERTY_KEEP_STALE_ACCOUNT_DATA))) {
4488b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki            Log.w(TAG, "Accounts changed, but not removing stale data for " +
4489b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki                    DEBUG_PROPERTY_KEEP_STALE_ACCOUNT_DATA);
4490b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki            return true;
4491b7e305c1ad38f1e2058453f7412e51477adf8a24Makoto Onuki        }
44929ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        Log.i(TAG, "Accounts changed");
449335997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
449435997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        invalidateFastScrollingIndexCache();
449535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
44969d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final ContactsDatabaseHelper dbHelper = mDbHelper.get();
44979d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final SQLiteDatabase db = dbHelper.getWritableDatabase();
44985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
44995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        db.beginTransaction();
45005dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro
45015dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro        // WARNING: This method can be run in either contacts mode or profile mode.  It is
45025dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro        // absolutely imperative that no calls be made inside the following try block that can
45035dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro        // interact with the contacts DB.  Otherwise it is quite possible for a deadlock to occur.
450470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
45059d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            // First, remove stale rows from raw_contacts, groups, and related tables.
45069d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
45079d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            // All accounts that are used in raw_contacts and/or groups.
45089d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            final Set<AccountWithDataSet> knownAccountsWithDataSets
45099d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    = dbHelper.getAllAccountsWithDataSets();
451048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
45119d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            // Find the accounts that have been removed.
45129d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            final List<AccountWithDataSet> accountsWithDataSetsToDelete = Lists.newArrayList();
45139d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            for (AccountWithDataSet knownAccountWithDataSet : knownAccountsWithDataSets) {
45149d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                if (knownAccountWithDataSet.isLocalAccount()
45159d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        || knownAccountWithDataSet.inSystemAccounts(systemAccounts)) {
45169d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    continue;
451743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                }
45189d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                accountsWithDataSetsToDelete.add(knownAccountWithDataSet);
451970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
452070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
452143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (!accountsWithDataSetsToDelete.isEmpty()) {
452243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
452343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    Log.d(TAG, "removing data for removed account " + accountWithDataSet);
45249d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    final Long accountIdOrNull = dbHelper.getAccountIdOrNull(accountWithDataSet);
45259d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
45269d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    // getAccountIdOrNull() really shouldn't return null here, but just in case...
45279d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    if (accountIdOrNull != null) {
45289d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        final String[] accountIdParams =
45299d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                new String[] {Long.toString(accountIdOrNull)};
45309d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        db.execSQL(
45319d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                "DELETE FROM " + Tables.GROUPS +
45329d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                " WHERE " + GroupsColumns.ACCOUNT_ID + " = ?",
45339d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                accountIdParams);
45349d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        db.execSQL(
45359d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                "DELETE FROM " + Tables.PRESENCE +
45369d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
45379d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        "SELECT " + RawContacts._ID +
45389d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        " FROM " + Tables.RAW_CONTACTS +
45399d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        " WHERE " + RawContactsColumns.ACCOUNT_ID + " = ?)",
45409d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        accountIdParams);
45419d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        db.execSQL(
45429d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                "DELETE FROM " + Tables.STREAM_ITEM_PHOTOS +
45439d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                " WHERE " + StreamItemPhotos.STREAM_ITEM_ID + " IN (" +
45449d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        "SELECT " + StreamItems._ID +
45459d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        " FROM " + Tables.STREAM_ITEMS +
45469d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
45479d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                                "SELECT " + RawContacts._ID +
45489d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                                " FROM " + Tables.RAW_CONTACTS +
45499d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                                " WHERE " + RawContactsColumns.ACCOUNT_ID + "=?))",
45509d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                                accountIdParams);
45519d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        db.execSQL(
45529d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                "DELETE FROM " + Tables.STREAM_ITEMS +
45539d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
45549d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        "SELECT " + RawContacts._ID +
45559d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        " FROM " + Tables.RAW_CONTACTS +
45569d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        " WHERE " + RawContactsColumns.ACCOUNT_ID + " = ?)",
45579d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                        accountIdParams);
45589d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        db.execSQL(
45599d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                "DELETE FROM " + Tables.RAW_CONTACTS +
45609d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                " WHERE " + RawContactsColumns.ACCOUNT_ID + " = ?",
45619d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                accountIdParams);
45629d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        db.execSQL(
45639d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                "DELETE FROM " + Tables.ACCOUNTS +
45649d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                " WHERE " + AccountsColumns._ID + "=?",
45659d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                accountIdParams);
45669d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    }
4567e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4568e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
456933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
457033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4571e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
457233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
45735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = db.rawQuery("SELECT " + Contacts._ID +
457433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
457533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
457669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
457769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
457869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
457933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
458033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
458169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
458269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
458333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
458433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
458533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
458633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
458733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
458833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
458933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
459033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
459133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
45925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
459333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
45949d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                dbHelper.updateAllVisible();
45955dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro
45965dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                // Don't bother updating the search index if we're in profile mode - there is no
45975dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                // search index for the profile DB, and updating it for the contacts DB in this case
45985dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                // makes no sense and risks a deadlock.
45995dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                if (!inProfileMode()) {
46009ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                    // TODO Fix it.  It only updates index for contacts/raw_contacts that the
46019ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                    // current transaction context knows updated, but here in this method we don't
46029ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                    // update that information, so effectively it's no-op.
46039ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                    // We can probably just schedule BACKGROUND_TASK_UPDATE_SEARCH_INDEX.
46049ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                    // (But make sure it's not scheduled yet. We schedule this task in initialize()
46059ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                    // too.)
46065dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                    updateSearchIndexInTransaction();
46075dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                }
460833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
460933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
46109d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            // Second, remove stale rows from Tables.SETTINGS and Tables.DIRECTORIES
46119d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            removeStaleAccountRows(Tables.SETTINGS, Settings.ACCOUNT_NAME, Settings.ACCOUNT_TYPE,
46129d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    systemAccounts);
46139d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            removeStaleAccountRows(Tables.DIRECTORIES, Directory.ACCOUNT_NAME,
46149d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    Directory.ACCOUNT_TYPE, systemAccounts);
461543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
46169d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            // Third, remaining tasks that must be done in a transaction.
46179d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            // TODO: Should sync state take data set into consideration?
46189d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            dbHelper.getSyncState().onAccountsChanged(db, systemAccounts);
46199ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki
46209ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            saveAccounts(systemAccounts);
462143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
46225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
462370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
46245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.endTransaction();
462570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
462673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
46273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
46289d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        updateContactsAccountCount(systemAccounts);
46299d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        updateProviderStatus();
46309d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        return true;
463170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4632619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
46333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
46343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
46353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
46363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
46373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
46383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
46393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
46403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
46413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
46423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
46433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
46443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
46453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
46463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
46473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
46483826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
46493826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
46503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
46513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
46523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
465372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
4654bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
4655d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4656d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
46579d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    public void removeStaleAccountRows(String table, String accountNameColumn,
46589d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            String accountTypeColumn, Account[] systemAccounts) {
46599d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
46609d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final Cursor c = db.rawQuery(
46619d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                "SELECT DISTINCT " + accountNameColumn +
46629d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                "," + accountTypeColumn +
466343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                " FROM " + table, null);
4664627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
46659d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            c.moveToPosition(-1);
4666627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
46679d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                final AccountWithDataSet accountWithDataSet = AccountWithDataSet.get(
46689d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        c.getString(0), c.getString(1), null);
46699d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                if (accountWithDataSet.isLocalAccount()
46709d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        || accountWithDataSet.inSystemAccounts(systemAccounts)) {
46719d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    // Account still exists.
46729d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    continue;
4673627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
46749d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
46759d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                db.execSQL("DELETE FROM " + table +
46769d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        " WHERE " + accountNameColumn + "=? AND " +
46779d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        accountTypeColumn + "=?",
46789d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        new String[] {accountWithDataSet.getAccountName(),
46799d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                                accountWithDataSet.getAccountType()});
4680627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4681627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4682627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4683627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
4684627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4685627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
46864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
46874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
46884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
468915826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann        return query(uri, projection, selection, selectionArgs, sortOrder, null);
469015826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann    }
469115826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann
469215826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann    @Override
469315826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
46947898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown            String sortOrder, CancellationSignal cancellationSignal) {
469547ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki        if (VERBOSE_LOGGING) {
469647ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            Log.v(TAG, "query uri=" + uri + " selection=" + selection + " order=" + sortOrder);
469747ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki        }
469815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
469915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
470015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
470136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
470236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamReadPermission(uri);
470336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
47045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Query the profile DB if appropriate.
47055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
47065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
470715826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann            return mProfileProvider.query(uri, projection, selection, selectionArgs, sortOrder,
47087898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                    cancellationSignal);
47095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
47105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
47115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Otherwise proceed with a normal query against the contacts DB.
47125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
47135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(mContactsHelper.getReadableDatabase());
4714d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4715385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
4716b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
471715826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1,
47187898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                    cancellationSignal));
4719385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
4720b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47213716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
47227898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                    Directory.DEFAULT, cancellationSignal));
4723d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
4724b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47253716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
47267898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                    Directory.LOCAL_INVISIBLE, cancellationSignal));
4727d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4728d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4729d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4730d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4731a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4732a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4733d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4734d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4735d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4736d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4737d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4738d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4739d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4740d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4741d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4742d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4743d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4744d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
47452e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
47462e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
47472e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
47482e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
47492e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
47502e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4751d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
475209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
475309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
475409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
475509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
475609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4757332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4758d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
47596ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
47606ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
47616ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
47626ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
47636ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4764b6b22df3a64a80531d58f9cd60f2872fc2af92d1Dave Santoro        // Load the cursor contents into a memory cursor (backed by a cursor window) and close the
4765b6b22df3a64a80531d58f9cd60f2872fc2af92d1Dave Santoro        // underlying cursor.
4766b6b22df3a64a80531d58f9cd60f2872fc2af92d1Dave Santoro        try {
4767b6b22df3a64a80531d58f9cd60f2872fc2af92d1Dave Santoro            MemoryCursor memCursor = new MemoryCursor(null, cursor.getColumnNames());
4768b6b22df3a64a80531d58f9cd60f2872fc2af92d1Dave Santoro            memCursor.fillFromCursor(cursor);
4769b6b22df3a64a80531d58f9cd60f2872fc2af92d1Dave Santoro            return memCursor;
4770b6b22df3a64a80531d58f9cd60f2872fc2af92d1Dave Santoro        } finally {
4771b6b22df3a64a80531d58f9cd60f2872fc2af92d1Dave Santoro            cursor.close();
4772547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
47733716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
47743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4775b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addSnippetExtrasToCursor(Uri uri, Cursor cursor) {
4776547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
4777547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
4778547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
4779b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return cursor;
4780547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
4781547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
47823716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
47833716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4784b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        // Snippet data is needed for the snippeting on the client side, so store it in the cursor
4785b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor && deferredSnippetingRequested(uri)){
4786b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4787b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4788b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4789b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4790b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4791b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putString(ContactsContract.DEFERRED_SNIPPETING_QUERY, query);
4792b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4793b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
47945517770250b3afa4fd88b6869c3244680821d222Dave Santoro        }
4795b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
4796b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
4797b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4798b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addDeferredSnippetingExtra(Cursor cursor) {
4799b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor){
4800b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4801b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4802b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4803b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4804b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4805b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putBoolean(ContactsContract.DEFERRED_SNIPPETING, true);
4806b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
4807b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
4808b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
48096ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
48106ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4811d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4812d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4813d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4814d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4815d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4816d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4817d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4818d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4819d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4820d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4821d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4822d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4823d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4824d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4825d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4826d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4827d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4828d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
48294458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
48304458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
48314458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
48325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
483349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
48344458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
48354458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
48364458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
48374458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
48384458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
48394458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
48404458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
48414458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
48424458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
48434458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
48444458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
48454458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
48464458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4847d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
48484458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4849d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4850d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
48514458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
48524458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4853d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4854d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
485572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
48564458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
48574458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
48584458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
485972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
486072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
486135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    protected Cursor queryLocal(final Uri uri, final String[] projection, String selection,
486235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            String[] selectionArgs, String sortOrder, final long directoryId,
486335997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            final CancellationSignal cancellationSignal) {
48640b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
48655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
48665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
4867078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getReadableDatabase());
48685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
486935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4870d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
48711f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4872c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4873b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean snippetDeferred = false;
4874c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
48752ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // The expression used in bundleLetterCountExtras() to get count.
48762ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        String addressBookIndexerCountExpression = null;
48772ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
4878a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
48794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
488035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
48815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
48825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().query(mActiveDb.get(), projection, selection,
48835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs, sortOrder);
488435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4885d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4886763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
48879ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                appendLocalDirectoryAndAccountSelectionIfNeeded(qb, directoryId, uri);
4888619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4889619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4890619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4891d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
48924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4893763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
48944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
48954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
48966bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
48976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
48986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
48995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
49005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
49015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
49025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
49035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
49045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
4905fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
49065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4907a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
49085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
49095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
49105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
49115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4912763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4913a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
49145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
4915a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
491615826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey,
49177898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                            cancellationSignal);
4918a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
49195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
49205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
49215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
49225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4923763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
49244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
49255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
49264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
49275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
49285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
49295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49302149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
4931bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_DATA:
4932bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
4933bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO: {
49342149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
49352149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
49362149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
49375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
49382149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
49392149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
49402149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
49412149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
49422149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
49432149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
49442149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
4945bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
4946cde9e5e83f7c3a6bf341f9f2fa2359a1ed127f95Daniel Lehmann                        lookupQb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
4947bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
4948a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
49495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
4950a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
495115826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey,
49527898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                            cancellationSignal);
4953a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
49542149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
49552149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
49562149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
49572149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
49582149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
49592149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
49602149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
49615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
49622149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
496324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
4964bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
4965bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
4966bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                }
49672149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
49682149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
49692149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
49702149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
49713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
49723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
49733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
49743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
4975af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                qb.appendWhere(StreamItems.CONTACT_ID + "=?");
49763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
49773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
49783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
49793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
49803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
49813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
49823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
49833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
49845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
49853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
49863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
49873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
49883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
49893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
49903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
49913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
49925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
49933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4994af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_ID, contactId,
499515826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann                            StreamItems.CONTACT_LOOKUP_KEY, lookupKey,
49967898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                            cancellationSignal);
49973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
49983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
49993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
50003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
50013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
50023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
50035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
50043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
50053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
50063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
50073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
50083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5009f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
501042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
50115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
5012ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
5013f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
50144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
501524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
50164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
5017f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
5018f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
5019f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
502042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
502142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
502242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
50235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().rawQuery(
502442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
502542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
502642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
502742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
502842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
502942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
5030ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
5031916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
5032b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                boolean deferredSnipRequested = deferredSnippetingRequested(uri);
5033ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
5034916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
5035ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
5036fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki
5037fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                // If the query consists of a single word, we can do snippetizing after-the-fact for
5038fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                // a performance boost.  Otherwise, we can't defer.
5039fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                snippetDeferred = isSingleWordQuery(filterParam)
5040fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    && deferredSnipRequested && snippetNeeded(projection);
50417ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
5042b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        qb, uri, projection, filterParam, directoryId,
5043fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                        snippetDeferred);
5044ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5045ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5046ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
5047ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
5048ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
50492f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
50502f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
50512f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
50522f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
50532f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
50542f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
50552f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
50562f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
50572f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
50584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
50594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5060e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
50615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
50622f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
50634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
50644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
50652f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
50665e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
50672f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
50685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
50695e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
50704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
50714928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
50724928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                qb.setProjectionMap(phoneOnly ?
50734928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        sStrequentPhoneOnlyStarredProjectionMap
50744928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        : sStrequentStarredProjectionMap);
50759dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                if (phoneOnly) {
50765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    qb.appendWhere(DbQueryUtils.concatenateClauses(
50775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            selection, Contacts.HAS_PHONE_NUMBER + "=1"));
50789dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                }
50792f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
508072c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                final String starredInnerQuery = qb.buildQuery(subProjection,
508172c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                        Contacts.STARRED + "=1", Contacts._ID, null,
508272c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                        Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC", null);
5083d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
50842f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
5085d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
50862f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
50874928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
508872c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                // Build the second query for frequent part. These JOINS can be very slow
508972c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                // if assembled in the wrong order. Be sure to test changes against huge databases.
509072c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                final String frequentInnerQuery;
50914928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                if (phoneOnly) {
50924928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    final StringBuilder tableBuilder = new StringBuilder();
50934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // In phone only mode, we need to look at view_data instead of
50944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // contacts/raw_contacts to obtain actual phone numbers. One problem is that
50954928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data is much larger than view_contacts, so our query might become much
50964928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // slower.
50974928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    //
50984928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // To avoid the possible slow down, we start from data usage table and join
50994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data to the table, assuming data usage table is quite smaller than
51004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // data rows (almost always it should be), and we don't want any phone
51014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // numbers not used by the user. This way sqlite is able to drop a number of
51024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // rows in view_data in the early stage of data lookup.
51034928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    tableBuilder.append(Tables.DATA_USAGE_STAT
51044928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
51054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
51064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataColumns.CONCRETE_ID + " AND "
51074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
51084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
51094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
51104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactStatusUpdateJoin(tableBuilder, projection,
51114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            ContactsColumns.LAST_STATUS_UPDATE_ID);
51124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
51134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setTables(tableBuilder.toString());
51144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
511572c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                    final long phoneMimeTypeId =
511672c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            mDbHelper.get().getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
511772c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                    final long sipMimeTypeId =
511872c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            mDbHelper.get().getMimeTypeId(SipAddress.CONTENT_ITEM_TYPE);
51194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
51204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
5121db0e85645ffe5a79500b31390149507ac432a2d9Makoto Onuki                            "(" + Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
512272c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            DataColumns.MIMETYPE_ID + " IN (" +
5123db0e85645ffe5a79500b31390149507ac432a2d9Makoto Onuki                            phoneMimeTypeId + ", " + sipMimeTypeId + ")) AND (" +
5124db0e85645ffe5a79500b31390149507ac432a2d9Makoto Onuki                            RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY + ")"));
512572c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                    frequentInnerQuery =
512672c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            qb.buildQuery(subProjection, null, null, null,
512772c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            TIMES_USED_SORT_COLUMN + " DESC", "25");
51284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                } else {
51294928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
51304928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
51314928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
51324928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
51335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)"));
5134db0e85645ffe5a79500b31390149507ac432a2d9Makoto Onuki                    // Note frequentInnerQuery is a grouping query, so the "IN default_directory"
5135db0e85645ffe5a79500b31390149507ac432a2d9Makoto Onuki                    // selection needs to be in HAVING, not in WHERE.
5136db0e85645ffe5a79500b31390149507ac432a2d9Makoto Onuki                    final String HAVING =
5137db0e85645ffe5a79500b31390149507ac432a2d9Makoto Onuki                            RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY;
513872c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                    frequentInnerQuery = qb.buildQuery(subProjection,
5139db0e85645ffe5a79500b31390149507ac432a2d9Makoto Onuki                            null, Contacts._ID, HAVING, null, "25");
51404928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                }
5141d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
514272c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                // We need to wrap the inner queries in an extra select, because they contain
514372c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                // their own SORT and LIMIT
514472c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                final String frequentQuery = "SELECT * FROM (" + frequentInnerQuery + ")";
514572c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                final String starredQuery = "SELECT * FROM (" + starredInnerQuery + ")";
514672c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann
5147d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
51482f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
514972c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery}, null, null);
51502f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
51512f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
51522f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
51532f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
51542f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
51552f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
51562f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
51572f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
51582f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
51592f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
51602f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
51612f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
51622f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
51637d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
51647d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
51652f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
51662f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
51675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = mActiveDb.get().rawQuery(unionQuery, doubledSelectionArgs);
51682f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
51692f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
5170d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
5171d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
51722f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
5173d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
5174d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
517545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            case CONTACTS_FREQUENT: {
517645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
517745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.setProjectionMap(sStrequentFrequentProjectionMap);
517845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                groupBy = Contacts._ID;
517945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                if (!TextUtils.isEmpty(sortOrder)) {
518045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
518145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                } else {
518245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY;
518345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                }
518445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                break;
518545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            }
518645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
5187ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
5188763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
5189b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
519071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
51917cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    String groupMimeTypeId = String.valueOf(
51927cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
51934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
51947cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    selectionArgs = insertSelectionArg(selectionArgs, groupMimeTypeId);
5195b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
5196b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
5197b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
5198b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
519924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
520024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
520124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
520224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
520324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
520424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
520524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
520624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
520724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
520824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
520924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
5210ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
521124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
521224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
521324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
521424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5215a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
52164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
521782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
52184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
52194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
52206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
52216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
522200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
5223a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
52243653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
522582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
52264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
52274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
52283653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
52293653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
52303653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
52313653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
5232a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
5233a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
5234a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5235a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5236a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
5237a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5238a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5239a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5240a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
5241a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
5242a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
5243a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
5244a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
52455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
5246a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
5247a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5248a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
5249a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
5250a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
5251a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
5252a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
5253a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
5254a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
52555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5256a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5257a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
525815826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann                            Contacts.Entity.LOOKUP_KEY, lookupKey,
52597898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                            cancellationSignal);
5260a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
5261a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
5262a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
5263a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5264a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5265a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5266a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
52675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
5268a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
5269a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5270a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5271a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
52723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
52733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
52743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
52753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
52763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
52773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
52783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
52793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
52809b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                qb.appendWhere(StreamItems._ID + "=?");
52813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
52823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
52833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
52843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
5285084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                return buildSingleRowResult(projection, new String[] {StreamItems.MAX_ITEMS},
5286084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                        new Object[] {MAX_STREAM_ITEMS_PER_RAW_CONTACT});
52873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
52883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
52893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
52903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
52913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
52923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
52933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
52943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
52953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
52963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
52973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
52983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
52993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
53033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
53043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
53053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
53063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
53073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
53083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
53093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
53103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5313f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case PHOTO_DIMENSIONS: {
5314084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                return buildSingleRowResult(projection,
5315084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                        new String[] {DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
5316c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki                        new Object[] {getMaxDisplayPhotoDim(), getMaxThumbnailDim()});
5317f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
5318f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
5319e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa            case PHONES:
5320e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa            case CALLABLES: {
5321e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                final String mimeTypeIsPhoneExpression =
5322e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForPhone();
5323e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                final String mimeTypeIsSipExpression =
5324e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForSip();
532582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
5326e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                if (match == CALLABLES) {
5327e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    qb.appendWhere(" AND ((" + mimeTypeIsPhoneExpression +
5328e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                            ") OR (" + mimeTypeIsSipExpression + "))");
5329e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                } else {
5330e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    qb.appendWhere(" AND " + mimeTypeIsPhoneExpression);
5331e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                }
53322ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
53338ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                final boolean removeDuplicates = readBooleanQueryParameter(
53348ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                        uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
53358ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                if (removeDuplicates) {
53368ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
53378ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
53388ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // In this case, because we dedupe phone numbers, the address book indexer needs
53398ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // to take it into account too.  (Otherwise headers will appear in wrong
53408ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // positions.)
53418ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // So use count(distinct pair(CONTACT_ID, PHONE NUMBER)) instead of count(*).
53428ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // But because there's no such thing as pair() on sqlite, we use
53438ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // CONTACT_ID || ',' || PHONE NUMBER instead.
53448ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // This only slows down the query by 14% with 10,000 contacts.
53458ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    addressBookIndexerCountExpression = "DISTINCT "
53468ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                            + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
53478ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                }
53482815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
53492815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
53502815f58f72f109790585931f601a63ddc02536a5Evan Millar
5351e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa            case PHONES_ID:
5352e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa            case CALLABLES_ID: {
5353e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                final String mimeTypeIsPhoneExpression =
5354e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForPhone();
5355e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                final String mimeTypeIsSipExpression =
5356e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForSip();
535782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
5359e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                if (match == CALLABLES_ID) {
5360e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    qb.appendWhere(" AND ((" + mimeTypeIsPhoneExpression +
5361e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                            ") OR (" + mimeTypeIsSipExpression + "))");
5362e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                } else {
5363e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    qb.appendWhere(" AND " + mimeTypeIsPhoneExpression);
5364e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                }
53654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
536648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
536748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
536848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5369e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa            case PHONES_FILTER:
5370e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa            case CALLABLES_FILTER: {
5371e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                final String mimeTypeIsPhoneExpression =
5372e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForPhone();
5373e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                final String mimeTypeIsSipExpression =
5374e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        DataColumns.MIMETYPE_ID + "=" + mDbHelper.get().getMimeTypeIdForSip();
5375e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa
537646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
5377dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                final int typeInt = getDataUsageFeedbackType(typeParam,
5378dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                        DataUsageStatColumns.USAGE_TYPE_INT_CALL);
537946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
5380e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                if (match == CALLABLES_FILTER) {
5381e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    qb.appendWhere(" AND ((" + mimeTypeIsPhoneExpression +
5382e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                            ") OR (" + mimeTypeIsSipExpression + "))");
5383e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                } else {
5384e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    qb.appendWhere(" AND " + mimeTypeIsPhoneExpression);
5385e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                }
5386e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa
5387ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
53884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
53894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5390a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
53915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
539245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
5393d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    final String ftsMatchQuery = SearchIndexManager.getFtsMatchQuery(
5394d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                            filterParam, FtsQueryBuilder.UNSCOPED_NORMALIZING);
5395d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    if (ftsMatchQuery.length() > 0) {
5396155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
5397155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5398155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5399155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5400155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5401155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5402d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                                " WHERE " + SearchIndexColumns.NAME + " MATCH '");
5403d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append(ftsMatchQuery);
5404d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append("')");
540545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
54065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
54075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5408892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
5409892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
5410e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        if (hasCondition) {
54115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
54125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
54135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
5414892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
5415892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
5416892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
5417892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
5418892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
541945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
542045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
542145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
5422e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    if (!TextUtils.isEmpty(filterParam) && match == CALLABLES_FILTER) {
5423e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        // If the request is via Callable uri, Sip addresses matching the filter
5424e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        // parameter should be returned.
5425e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        if (hasCondition) {
5426e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                            sb.append(" OR ");
5427e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        }
5428e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        sb.append("(");
5429e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        sb.append(mimeTypeIsSipExpression);
5430e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        sb.append(" AND ((" + Data.DATA1 + " LIKE ");
5431e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
5432e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        sb.append(") OR (" + Data.DATA1 + " LIKE ");
5433e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        // Users may want SIP URIs starting from "sip:"
5434e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        DatabaseUtils.appendEscapedSQLString(sb, "sip:"+ filterParam + '%');
5435e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        sb.append(")))");
5436e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        hasCondition = true;
5437e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    }
5438e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa
543945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
544045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
544145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
544245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
54435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
54445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5445a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
5446ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
5447e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                if (match == CALLABLES_FILTER) {
5448e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    // If the row is for a phone number that has a normalized form, we should use
5449e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    // the normalized one as PHONES_FILTER does, while we shouldn't do that
5450e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    // if the row is for a sip address.
5451e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    String isPhoneAndHasNormalized = "("
5452e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                        + mimeTypeIsPhoneExpression + " AND "
5453fc42772538fc6f7e2e444cbe6b24e06a3fbf933dMakoto Onuki                        + Phone.NORMALIZED_NUMBER + " IS NOT NULL)";
5454e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                    groupBy = "(CASE WHEN " + isPhoneAndHasNormalized
5455fc42772538fc6f7e2e444cbe6b24e06a3fbf933dMakoto Onuki                        + " THEN " + Phone.NORMALIZED_NUMBER
5456fc42772538fc6f7e2e444cbe6b24e06a3fbf933dMakoto Onuki                        + " ELSE " + Phone.NUMBER + " END), " + RawContacts.CONTACT_ID;
5457e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                } else {
5458fc42772538fc6f7e2e444cbe6b24e06a3fbf933dMakoto Onuki                    groupBy = "(CASE WHEN " + Phone.NORMALIZED_NUMBER
5459fc42772538fc6f7e2e444cbe6b24e06a3fbf933dMakoto Onuki                        + " IS NOT NULL THEN " + Phone.NORMALIZED_NUMBER
546058567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " ELSE " + Phone.NUMBER + " END), " + RawContacts.CONTACT_ID;
5461e432023d408c461295e53c0593fabb2b4c17aeb3Daisuke Miyakawa                }
5462a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
546346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
546446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
546546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
546646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
546746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
546846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
5469a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
5470ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5471ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5472ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
54734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
547482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54757cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54767cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
54778ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
54788ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                final boolean removeDuplicates = readBooleanQueryParameter(
54798ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                        uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
54808ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                if (removeDuplicates) {
54818ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
54828ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
54838ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // See PHONES for more detail.
54848ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    addressBookIndexerCountExpression = "DISTINCT "
54858ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                            + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
54868ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                }
54874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
54884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
54894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
549048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
549182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
54937cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54947cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail()
54954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
549648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
549748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
549848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
54995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
550082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55017cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55027cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
55034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
550408768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
55055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    String address = mDbHelper.get().extractAddressFromEmailAddress(email);
550608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
550708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
55084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
5509071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                // unless told otherwise, we'll return visible before invisible contacts
5510071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                if (sortOrder == null) {
5511071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                    sortOrder = "(" + RawContacts.CONTACT_ID + " IN " +
5512071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                            Tables.DEFAULT_DIRECTORY + ") DESC";
5513071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                }
5514ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5515ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5516ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
55175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
551846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
5519dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                final int typeInt = getDataUsageFeedbackType(typeParam,
5520dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                        DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
552146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
552207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
55237d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
552407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
552507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
552607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
552707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
552807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
552907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
55305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
553107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
553207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
553307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
553407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
553507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
553607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
553707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
553807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
553907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
55402a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
55415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    sb.append(mDbHelper.get().getMimeTypeIdForEmail());
55422a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
554307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
554420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
5545155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
5546155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
5547155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
5548155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
55495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        sb.append(mDbHelper.get().getMimeTypeIdForEmail());
5550155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
5551155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5552155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5553155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5554155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5555155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5556d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                                " WHERE " + SearchIndexColumns.NAME + " MATCH '");
5557d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        final String ftsMatchQuery = SearchIndexManager.getFtsMatchQuery(
5558d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                                filterParam, FtsQueryBuilder.UNSCOPED_NORMALIZING);
5559d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append(ftsMatchQuery);
5560d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append("')");
55615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
55625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5563a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
55645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
55655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
5566a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
556746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
556846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
556946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
55707d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
55717d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
55727d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
5573a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
55745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
55755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
55765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5577ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
557882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55797cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55807cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
55818ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
55828ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                final boolean removeDuplicates = readBooleanQueryParameter(
55838ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                        uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
55848ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                if (removeDuplicates) {
55858ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
55868ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
55878ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // See PHONES for more detail.
55888ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    addressBookIndexerCountExpression = "DISTINCT "
55898ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                            + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
55908ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                }
5591ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5592ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5593ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
559448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
559582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
55977cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55987cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
55994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
560048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
560148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
560248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5603d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
5604d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
5605763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
56064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
56074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
56084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5609d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
5610d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
56115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
5612763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
56134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
56154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
56164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
56174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5618193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki            case RAW_CONTACTS_ID_DATA:
5619d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
5620193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki                int segment = match == RAW_CONTACTS_ID_DATA ? 1 : 2;
5621d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(segment));
562282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
562524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
562624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
562724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
56283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
56293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
56303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
56313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
56333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
56343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
563524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
563682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
563782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
563882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long streamItemId = Long.parseLong(uri.getPathSegments().get(3));
563982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                setTablesAndProjectionMapForStreamItems(qb);
564082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(streamItemId));
5641c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
564282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=? AND " +
564382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems._ID + "=?");
564482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
564582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
564682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
564724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
564824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
564924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
565024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
56515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                qb.appendWhere(" AND " + RawContacts._ID + "=?");
5652e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5653e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5654e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5655d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA:
5656d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA: {
565782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
5658e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5659e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5660e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5661d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA_ID:
5662d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
566382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
56654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
5666a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
5667a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
5668a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
566985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            case PROFILE_PHOTO: {
567085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
567185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
567285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                break;
567385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            }
567485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro
5675a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
5676e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
5677e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
5678e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
567958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                if (uri.getBooleanQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false)) {
568058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    if (TextUtils.isEmpty(sortOrder)) {
568158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // Default the sort order to something reasonable so we get consistent
568258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // results when callers don't request an ordering
568358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        sortOrder = Contacts.DISPLAY_NAME + " ASC";
568458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    }
568558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
568658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String sipAddress = uri.getPathSegments().size() > 1
568758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            ? Uri.decode(uri.getLastPathSegment()) : "";
568858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    setTablesAndProjectionMapForData(qb, uri, null, false, true);
568958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    StringBuilder sb = new StringBuilder();
569058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    selectionArgs = mDbHelper.get().buildSipContactQuery(sb, sipAddress);
569158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    selection = sb.toString();
569258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                } else {
569358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    if (TextUtils.isEmpty(sortOrder)) {
569458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // Default the sort order to something reasonable so we get consistent
569558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // results when callers don't request an ordering
569658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        sortOrder = " length(lookup.normalized_number) DESC";
569758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    }
569858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
569958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String number = uri.getPathSegments().size() > 1
570058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            ? uri.getLastPathSegment() : "";
570158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
570258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            mDbHelper.get().getCurrentCountryIso());
570358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String normalizedNumber =
570458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            PhoneNumberUtils.normalizeNumber(number);
570558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    mDbHelper.get().buildPhoneLookupAndContactQuery(
570658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            qb, normalizedNumber, numberE164);
570758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    qb.setProjectionMap(sPhoneLookupProjectionMap);
570856abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro
570956abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                    // Peek at the results of the first query (which attempts to use fully
571056abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                    // normalized and internationalized numbers for comparison).  If no results
571156abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                    // were returned, fall back to doing a match of the trailing 7 digits.
571256abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                    qb.setStrict(true);
571356abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                    boolean foundResult = false;
571456abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                    Cursor cursor = query(mActiveDb.get(), qb, projection, selection, selectionArgs,
57157898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                            sortOrder, groupBy, limit, cancellationSignal);
571656abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                    try {
571756abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                        if (cursor.getCount() > 0) {
571856abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                            foundResult = true;
571956abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                            return cursor;
572056abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                        } else {
572156abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                            qb = new SQLiteQueryBuilder();
572256abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                            mDbHelper.get().buildMinimalPhoneLookupAndContactQuery(
572356abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                                    qb, normalizedNumber);
572456abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                            qb.setProjectionMap(sPhoneLookupProjectionMap);
572556abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                        }
572656abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                    } finally {
572756abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                        if (!foundResult) {
572856abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                            // We'll be returning a different cursor, so close this one.
572956abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                            cursor.close();
573056abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                        }
573156abe81ced08c7af625b3eb8dd543f9030da9badDave Santoro                    }
573258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                }
5733a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
5734a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
5735a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5736ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
5737ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5738ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
57399ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                appendAccountIdFromParameter(qb, uri);
5740ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5741ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5742ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5743ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
5744ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5745ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
57464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
57474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
5748ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5749ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5750ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5751ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
575223ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                String tables = Views.GROUPS + " AS " + Tables.GROUPS;
5753ac2a6e814edb3d5e5bcca28d7d3f3977a489c2edMakoto Onuki                if (ContactsDatabaseHelper.isInProjection(projection, Groups.SUMMARY_COUNT)) {
575423ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                    tables = tables + Joins.GROUP_MEMBER_COUNT;
575523ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                }
575618b09495f5f37b38ff2e1c965e087dfde68c27fbMakoto Onuki                if (ContactsDatabaseHelper.isInProjection(projection,
575718b09495f5f37b38ff2e1c965e087dfde68c27fbMakoto Onuki                        Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT)) {
575818b09495f5f37b38ff2e1c965e087dfde68c27fbMakoto Onuki                    // TODO Add join for this column too (and update the projection map)
575918b09495f5f37b38ff2e1c965e087dfde68c27fbMakoto Onuki                    // TODO Also remove Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT when it works.
576018b09495f5f37b38ff2e1c965e087dfde68c27fbMakoto Onuki                    Log.w(TAG, Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT + " is not supported yet");
576118b09495f5f37b38ff2e1c965e087dfde68c27fbMakoto Onuki                }
576223ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                qb.setTables(tables);
576318b09495f5f37b38ff2e1c965e087dfde68c27fbMakoto Onuki                qb.setProjectionMap(sGroupsSummaryProjectionMap);
57649ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                appendAccountIdFromParameter(qb, uri);
5765f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                groupBy = GroupsColumns.CONCRETE_ID;
5766ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5767ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5768ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5769b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
57700c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
5771b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
5772b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
5773b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
5774b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
577531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
5776d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
57772d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
57782d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
57792d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
57802d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
578131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
5782d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
5783d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
578431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
578531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
578631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
578731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
57885b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
57895b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
57905b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
57915b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
57925b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
57935b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
57945b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
57955b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
579676dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
57975b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
57985b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
57995b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
58005b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
58015b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
58025b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
58035b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5804763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
58057581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
58065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mAggregator.get().queryAggregationSuggestions(qb, projection, contactId,
58075b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
580831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
580931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5810eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5811eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5812eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
5813f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5814e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5815e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5816e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
58175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final String groupMembershipMimetypeId = Long.toString(mDbHelper.get()
5818e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
581982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
58205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5821e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5822e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
582382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
58245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(
58255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                                projection, Settings.UNGROUPED_WITH_PHONES)) {
5826e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5827e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5828e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5829eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5830eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5831eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
58325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
58335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
58340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
58355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
58365ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
58375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
583882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
58390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
58404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
58414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
58425ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
58435ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
58445ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5845c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5846174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
58475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), uri, projection, limit);
5848c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5849c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5850c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
58512d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
5852174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
5853174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
5854174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
58555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), projection, lookupKey, filter);
5856c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5857c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
58583202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case RAW_CONTACT_ENTITIES:
58593202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case PROFILE_RAW_CONTACT_ENTITIES: {
5860a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
586146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
586246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
586346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
5864193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki            case RAW_CONTACT_ID_ENTITY: {
586546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5866a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
58674da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
58684da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
586946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
587046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
587146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
587209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
5873084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                return buildSingleRowResult(projection,
5874084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                        new String[] {ProviderStatus.STATUS, ProviderStatus.DATA1},
5875084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                        new Object[] {mProviderStatus, mEstimatedStorageRequirement});
587609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
587709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5878d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5879d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5880d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5881d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5882d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5883d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5884d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5885385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5886d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5887d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5888385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5889d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5890d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5891d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5892d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
58937a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
58947a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
58957a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
58967a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
58974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5898f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5899c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
59004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
59014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
590209e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
59037f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
5904ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
59055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                query(mActiveDb.get(), qb, projection, selection, selectionArgs, sortOrder, groupBy,
59067898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                limit, cancellationSignal);
5907b6b22df3a64a80531d58f9cd60f2872fc2af92d1Dave Santoro
5908ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
590935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            bundleFastScrollingIndexExtras(cursor, uri, mActiveDb.get(), qb, selection,
591035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                    selectionArgs, sortOrder, addressBookIndexerCountExpression,
591135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                    cancellationSignal);
5912ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5913b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetDeferred) {
5914b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            cursor = addDeferredSnippetingExtra(cursor);
5915b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
5916b6b22df3a64a80531d58f9cd60f2872fc2af92d1Dave Santoro
5917ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
59185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
59195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
59205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
59215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
59227898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown            String limit, CancellationSignal cancellationSignal) {
5923038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5924038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5925038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5926038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
59275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
59287898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                sortOrder, limit, cancellationSignal);
59294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
59304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
59314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
59324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
59334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
59344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
593509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5936a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5937a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5938a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5939a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
59407898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown     * @param cancellationSignal
5941a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5942a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5943a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5944a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5945a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
594615826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey,
59477898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown            CancellationSignal cancellationSignal) {
5948a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5949a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5950a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5951a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5952a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5953a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5954a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5955a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5956a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5957a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5958a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
59597898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                groupBy, limit, cancellationSignal);
5960a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5961a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5962a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5963a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5964a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5965a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5966a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
596709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
596835997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    private void invalidateFastScrollingIndexCache() {
596935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        if (VERBOSE_LOGGING) {
597035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            Log.v(TAG, "invalidatemFastScrollingIndexCache");
597135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        }
59725acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki
59735acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // FastScrollingIndexCache is thread-safe, no need to synchronize here.
59745acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        mFastScrollingIndexCache.invalidate();
597535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    }
597635997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
597735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    /**
597835997f3fdee2984b6d5373326110eda26929001aMakoto Onuki     * Add the "fast scrolling index" bundle, generated by {@link #getFastScrollingIndexExtras},
597935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki     * to a cursor as extras.  It first checks {@link FastScrollingIndexCache} to see if we
598035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki     * already have a cached result.
598135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki     */
598235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    private void bundleFastScrollingIndexExtras(Cursor cursor, Uri queryUri,
598335997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            final SQLiteDatabase db, SQLiteQueryBuilder qb, String selection,
598435997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            String[] selectionArgs, String sortOrder, String countExpression,
598535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            CancellationSignal cancellationSignal) {
598635997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        if (!(cursor instanceof AbstractCursor)) {
598735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            Log.w(TAG, "Unable to bundle extras.  Cursor is not AbstractCursor.");
598835997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            return;
598935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        }
599035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        Bundle b;
59915acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // Note even though FastScrollingIndexCache is thread-safe, we really need to put the
59925acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // put-get pair in a single synchronized block, so that even if multiple-threads request the
59935acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // same index at the same time (which actually happens on the phone app) we only execute
59945acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // the query once.
59955acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        //
59965acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // This doesn't cause deadlock, because only reader threads get here but not writer
59975acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // threads.  (Writer threads may call invalidateFastScrollingIndexCache(), but it doesn't
59985acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // synchronize on mFastScrollingIndexCache)
59995acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        //
60005acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // All reader and writer threads share the single lock object internally in
60015acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // FastScrollingIndexCache, but the lock scope is limited within each put(), get() and
60025acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // invalidate() call, so it won't deadlock.
60035acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki
60045acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // Synchronizing on a non-static field is generally not a good idea, but nobody should
60055acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        // modify mFastScrollingIndexCache once initialized, and it shouldn't be null at this point.
60065acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki        synchronized (mFastScrollingIndexCache) {
600735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            // First, try the cache.
600835997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            mFastScrollingIndexCacheRequestCount++;
600935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            b = mFastScrollingIndexCache.get(queryUri, selection, selectionArgs, sortOrder,
601035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                    countExpression);
601135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
601235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            if (b == null) {
601335997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                mFastScrollingIndexCacheMissCount++;
601435997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                // Not in the cache.  Generate and put.
601535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                final long start = System.currentTimeMillis();
601635997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
601735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                b = getFastScrollingIndexExtras(queryUri, db, qb, selection, selectionArgs,
60185acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki                        sortOrder, countExpression, cancellationSignal, getLocale());
601935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
602035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                final long end = System.currentTimeMillis();
602135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                final int time = (int) (end - start);
602235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                mTotalTimeFastScrollingIndexGenerate += time;
602335997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                if (VERBOSE_LOGGING) {
602435997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                    Log.v(TAG, "getLetterCountExtraBundle took " + time + "ms");
602535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                }
60265acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki                mFastScrollingIndexCache.put(queryUri, selection, selectionArgs, sortOrder,
60275acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki                        countExpression, b);
602835997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            }
602935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        }
603035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        ((AbstractCursor) cursor).setExtras(b);
603135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    }
603235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
6033bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
6034bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
6035bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
6036bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
6037ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6038bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
6039bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
6040ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
6041ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6042bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
6043bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
6044bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
6045bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
60465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // The first letter of the sort key column is what is used for the index headings.
60475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        public static final String SECTION_HEADING = "SUBSTR(%1$s,1,1)";
604824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
6049de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
6050ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
6051ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6052ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
605335997f3fdee2984b6d5373326110eda26929001aMakoto Onuki     * Computes counts by the address book index titles and returns it as {@link Bundle} which
60545acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki     * will be appended to a {@link Cursor} as extras.
6055ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
605635997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    private static Bundle getFastScrollingIndexExtras(final Uri queryUri, final SQLiteDatabase db,
605735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki            final SQLiteQueryBuilder qb, final String selection, final String[] selectionArgs,
60585acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki            final String sortOrder, String countExpression,
60595acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki            final CancellationSignal cancellationSignal, final Locale currentLocale) {
6060ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
6061ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6062ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
6063ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
6064ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
6065ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
6066ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
6067ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
6068ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
6069ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
6070ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
6071ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
6072ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
6073ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
6074ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
6075ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
6076ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
6077ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6078ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
6079a99ffbd887e5120951845e5c60c32f459f71e9f2Makoto Onuki        String sectionHeading = String.format(Locale.US, AddressBookIndexQuery.SECTION_HEADING,
6080a99ffbd887e5120951845e5c60c32f459f71e9f2Makoto Onuki                sortKey);
6081bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
608224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
6083bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
60842ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // If "what to count" is not specified, we just count all records.
60852ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        if (TextUtils.isEmpty(countExpression)) {
60862ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            countExpression = "*";
60872ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        }
60882ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
6089bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
6090bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
6091bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
6092bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
6093bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
6094bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
6095bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
6096ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
609735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + currentLocale.toString() + "')"
6098bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
6099ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
61002ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                "COUNT(" + countExpression + ") AS " + AddressBookIndexQuery.COUNT);
6101ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
6102ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6103f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
6104ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
610515826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix,
61067898a8e97722d5bf0db3c04107b2957b998c0332Jeff Brown                null, cancellationSignal);
6107ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6108ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
6109f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
6110ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
6111ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
6112bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
6113bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
6114bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6115bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
6116bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
6117bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
6118ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
6119f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
6120bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
612135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                if (title == null) {
612235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                    title = "";
612335997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                }
6124bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
6125bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
6126bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
6127bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
6128bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
6129bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
6130bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
6131bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
6132bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
6133bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6134bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
6135bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
6136bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
6137bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
6138bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6139bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
6140bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
6141bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
6142ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
61435acb3e091bc334d4e0f367a36f8d62a0add02b39Makoto Onuki            return FastScrollingIndexCache.buildExtraBundle(titles, counts);
6144ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
6145f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
6146ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
6147ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
6148ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
61492d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
615092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
615192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
615292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
615392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
61542d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
61552d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
61565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
61575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
61585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
615992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
61605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_PROFILE)) {
61615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // We should already be in a profile database context, so just look up a single contact.
61625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro           contactId = lookupSingleContactId(db);
61635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
61645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
616592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
616692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
616792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
616892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
616992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
617092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
617192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
617292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
617392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
617492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
617592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
617692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
617792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
617892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
617992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
618092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
618192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
618292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
61835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
61845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
61875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
61885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long lookupSingleContactId(SQLiteDatabase db) {
61905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = db.query(Tables.CONTACTS, new String[] {Contacts._ID},
61915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                null, null, null, null, null, "1");
61925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        try {
61935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (c.moveToFirst()) {
61945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return c.getLong(0);
61955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            } else {
61965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return -1;
61975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
61985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } finally {
61995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c.close();
62005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
62015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
62025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
62035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
620443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
62055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
62075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
620843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
62095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
62105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
62115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
62125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
621443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
62155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
62165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
62175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
62205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
62215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
62225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
62235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
62245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
622592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
62265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
62275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
62285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
62315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
62325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
62345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
62355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
62365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
623743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
623843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
62395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
62405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
624143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
62425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
62435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
62445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
624592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
624692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
62475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
62485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
62495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
62505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
62515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
62525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
62545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
62555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
62585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
626092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
626143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
62625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
62645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
626543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
62665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
626792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
62685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
62695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
627143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
62725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
627392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
62745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
627692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
627792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
627892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
627992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
62805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
62815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
628292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
628392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
628492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
62855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
628792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
628892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
62895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
629092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
629192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
629292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
629392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
629443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet = c.getString(
629543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        LookupByRawContactIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
629692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
629792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
629843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
629992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
630092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
630192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
630292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
630392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
630492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
630592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
630692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
630792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
630892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
630992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
631092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
631192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
63125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
631492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
631592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
631692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
631792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
631892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
631992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
632092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
632192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
632243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
632392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
632492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
632592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
632692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
632792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
632843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
632992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
633092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
633192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
633292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
633392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
633492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
63355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
63365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
63375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
63385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
633992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
634092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
63415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
63425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
63435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
63445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
63465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
63475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
63485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
63505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
63515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
63525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
635343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
635443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET);
63555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
63565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
635743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
63585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
63595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
63605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
636192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
636292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
636392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
63645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
63655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
63665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
63675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
63685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
63695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
63705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
63715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
63725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
63755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
63765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
637792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
637892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
637992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
638092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
638192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
638292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
638392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
638492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
638592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
638692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
638792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
63885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
63895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
63905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
63915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
63925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
63935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
63955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
63965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
63985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
63995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
64005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
64015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
64025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
64035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
64045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
64055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
64065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
64075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
64085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
64095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
64105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
64115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
64125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
64135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
64145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
64155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
64165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
64175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
64185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
64195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
64205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
64215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
64225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
6423763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
6424763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
64254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false);
64262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
64272f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
64282f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
64294928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * @param includeDataUsageStat true when the table should include DataUsageStat table.
64304928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Note that this uses INNER JOIN instead of LEFT OUTER JOIN, so some of data in Contacts
64314928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * may be dropped.
64322f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
64332f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
64344928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            String[] projection, boolean includeDataUsageStat) {
643582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
643672c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann        if (includeDataUsageStat) {
643772c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann            sb.append(Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT);
643872c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann            sb.append(" INNER JOIN ");
643972c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann        }
644072c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann
6441ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
64422f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
64432f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
64444928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        if (includeDataUsageStat) {
644572c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann            sb.append(" ON (" +
64462f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
64472f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
64484928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID) +
64492f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
64502f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
64512f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
64527ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
64537ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6454916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
6455916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
6456916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
6457916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6458916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
6459916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
6460916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
6461916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
6462916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
6463fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki            String[] projection, String filter, long directoryId, boolean deferSnippeting) {
64647ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
64657ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6466ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
6467916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
646803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
646903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
647003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
647103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
647230cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
647330cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
64745e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
6475fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki            appendSearchIndexJoin(sb, uri, projection, filter, deferSnippeting);
64765e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
64777ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
64787ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
647903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
648003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
648103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
6482916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
648303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
6484b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            StringBuilder sb, Uri uri, String[] projection, String filter,
6485fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki            boolean  deferSnippeting) {
6486916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6487b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetNeeded(projection)) {
648803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
648903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
649003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
649103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
649203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
649303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
649403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
64955e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
64965e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
64975e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
64985e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
64995e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
65005e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
65015e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
65025e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
65035e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6504174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
6505b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens,
6506fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    deferSnippeting);
6507174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6508b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, filter, false, null, null, null, 0, false);
6509174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6510174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
6511174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6512174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
6513174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
6514fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki            int maxTokens, boolean deferSnippeting) {
6515174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
6516174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
6517174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
6518174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
6519174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
6520174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
65213716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6522174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
65235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            emailAddress = mDbHelper.get().extractAddressFromEmailAddress(filter);
6524174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
6525174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6526174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
652704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
652804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
652904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
65305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().getCountryIso());
653104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
6532174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6533174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6534d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        final String SNIPPET_CONTACT_ID = "snippet_contact_id";
6535d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS " + SNIPPET_CONTACT_ID);
6536174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
65375e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
65385e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
65393d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
6540fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                if (!deferSnippeting) {
6541fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    // Add the snippet marker only when we're really creating snippet.
6542fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    DatabaseUtils.appendEscapedSQLString(sb, startMatch);
6543fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    sb.append("||");
6544fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                }
6545fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                sb.append("(SELECT MIN(" + Email.ADDRESS + ")");
654604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
654704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
654804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
654904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
6550fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                sb.append(")");
6551fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                if (!deferSnippeting) {
6552fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    sb.append("||");
6553fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    DatabaseUtils.appendEscapedSQLString(sb, endMatch);
6554fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                }
65553d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
65563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6557fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                if (deferSnippeting) {
65583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
65593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
65603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
65613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
65623d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
65633d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
65643d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
6565fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                if (!deferSnippeting) {
6566fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    // Add the snippet marker only when we're really creating snippet.
6567fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    DatabaseUtils.appendEscapedSQLString(sb, startMatch);
6568fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    sb.append("||");
6569fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                }
6570fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                sb.append("(SELECT MIN(" + Phone.NUMBER + ")");
657104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
657204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
657304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
657404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
657504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
657604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
657704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
657804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
657904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
658004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
658104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
658204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
658304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
658404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
6585fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                sb.append(")");
6586fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                if (! deferSnippeting) {
6587fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    sb.append("||");
6588fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    DatabaseUtils.appendEscapedSQLString(sb, endMatch);
6589fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                }
65905e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
65913716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6592fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                if (deferSnippeting) {
65933716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
65943716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
65953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
65963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
65975e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
659803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
659904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
660004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
6601fba89ea92f519d77ec1d762724ed11bf4ebb7d20Makoto Onuki                    if (deferSnippeting) {
66023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
66033716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
66043716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
66053716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
66063716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
66073716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
66083716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
66093716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
66103716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
66113716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
66123716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
66133716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
66143716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
66153716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
66163716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
66173716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
661804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
661904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
662004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
662103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
66225e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
66235e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
662403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
66255e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
66265e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
6627d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        sb.append(Tables.SEARCH_INDEX + " MATCH '");
66285e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
6629d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // we know that the emailAddress contains a @. This phrase search should be
6630d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // scoped against "content:" only, but unfortunately SQLite doesn't support
6631d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // phrases and scoped columns at once. This is fine in this case however, because:
6632d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            //  - We can't erronously match against name, as name is all-hex (so the @ can't match)
6633d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            //  - We can't match against tokens, because phone-numbers can't contain @
6634d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String sanitizedEmailAddress =
6635d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    emailAddress == null ? "" : sanitizeMatch(emailAddress);
6636d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append("\"");
6637d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append(sanitizedEmailAddress);
6638d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append("*\"");
66393d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
6640d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // normalized version of the phone number (phoneNumber can only have + and digits)
6641d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String phoneNumberCriteria = " OR tokens:" + phoneNumber + "*";
6642d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann
6643d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // international version of this number (numberE164 can only have + and digits)
6644d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String numberE164Criteria =
6645d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    (numberE164 != null && !TextUtils.equals(numberE164, phoneNumber))
6646d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    ? " OR tokens:" + numberE164 + "*"
6647d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    : "";
6648d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann
6649d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // combine all criteria
6650d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String commonCriteria =
6651d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    phoneNumberCriteria + numberE164Criteria;
6652d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann
6653d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // search in content
6654d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append(SearchIndexManager.getFtsMatchQuery(filter,
6655d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    FtsQueryBuilder.getDigitsQueryBuilder(commonCriteria)));
665603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
6657d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // general case: not a phone number, not an email-address
6658d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append(SearchIndexManager.getFtsMatchQuery(filter,
6659d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    FtsQueryBuilder.SCOPED_NAME_NORMALIZING));
66609c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
66611322df8f90d80587748ad10539516635326c01e8Daniel Lehmann        // Omit results in "Other Contacts".
66621322df8f90d80587748ad10539516635326c01e8Daniel Lehmann        sb.append("' AND " + SNIPPET_CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY + ")");
66631322df8f90d80587748ad10539516635326c01e8Daniel Lehmann        sb.append(" ON (" + Contacts._ID + "=" + SNIPPET_CONTACT_ID + ")");
6664a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
6665a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
6666d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann    private static String sanitizeMatch(String filter) {
6667d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        return filter.replace("'", "").replace("*", "").replace("-", "").replace("\"", "");
66682352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
66692352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
66705e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
66715e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
66725e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
66735e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
66745e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
66755e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
66765e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
66775e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
66785e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
66795e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
66805e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
66815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
66825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
66835e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
66845e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6685763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
6686763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
6687ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
6688763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
6689763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
66909ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        appendAccountIdFromParameter(qb, uri);
6691763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
6692763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
6693a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
6694ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
6695a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
66969ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        appendAccountIdFromParameter(qb, uri);
669746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
669846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
669982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
670082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
670158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        setTablesAndProjectionMapForData(qb, uri, projection, distinct, false, null);
670258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    }
670358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
670458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
670558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            String[] projection, boolean distinct, boolean addSipLookupColumns) {
670658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        setTablesAndProjectionMapForData(qb, uri, projection, distinct, addSipLookupColumns, null);
670746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
670846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
670946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
671046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
671146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
671246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
671346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
671446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
671558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        setTablesAndProjectionMapForData(qb, uri, projection, distinct, false, usageType);
671658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    }
671758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
671858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
671958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            String[] projection, boolean distinct, boolean addSipLookupColumns, Integer usageType) {
672082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6721ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
672282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
672382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
6724a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
6725a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6726a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6727a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
67283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
672946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
673046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
673146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
673246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
673382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
6734f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
6735f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
67365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                || !mDbHelper.get().isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
6737f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
673858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
673958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        final ProjectionMap projectionMap;
674058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        if (addSipLookupColumns) {
674158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            projectionMap = useDistinct
674258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    ? sDistinctDataSipLookupProjectionMap : sDataSipLookupProjectionMap;
674358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        } else {
674458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            projectionMap = useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap;
674558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        }
674658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
674758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        qb.setProjectionMap(projectionMap);
67489ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        appendAccountIdFromParameter(qb, uri);
6749ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
6750ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
67510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
67520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
67530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6754ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
67550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
6756a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6757a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
67580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6759a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6760a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
6761a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6762a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
67633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
67649b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann        qb.setTables(Views.STREAM_ITEMS);
67653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
67663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
67673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
67683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
67691dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.PHOTO_FILES
67701dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.STREAM_ITEM_PHOTOS + " ON ("
67711dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_PHOTO_FILE_ID + "="
67721dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + PhotoFilesColumns.CONCRETE_ID
67731dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.STREAM_ITEMS + " ON ("
67741dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "="
67750bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_ID + ")"
67760bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
67770bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
67780bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + ")");
67793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
67803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
67813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
6782a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
6783a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
6784a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6785ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
6786a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
6787a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6788a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
6789a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6790a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
6791a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
6792a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6793a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6794a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
67959ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        appendAccountIdFromParameter(qb, uri);
6796a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6797a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6798a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
6799a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
68005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6801a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
6802a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
6803a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
6804a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
6805a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
6806a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
6807a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
6808a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
6809a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
68100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6811a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
68120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6813a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
6814a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
68155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
68160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
68170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
68180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
68190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
68200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
68210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
6822a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
6823a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
68240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6825a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6826a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
682746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
682846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
682946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
683046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
683146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
683246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
6833a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
6834a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
68355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6836a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
6837a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
6838a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
6839a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
6840a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6841a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6842a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6843a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
6844a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
68455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
6846a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
6847a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
6848a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6849a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6850a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
68519ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    private void appendLocalDirectoryAndAccountSelectionIfNeeded(SQLiteQueryBuilder qb,
68529ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            long directoryId, Uri uri) {
68539ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final StringBuilder sb = new StringBuilder();
6854385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
68559ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            sb.append("(" + Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY + ")");
6856385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
68579ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            sb.append("(" + Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY + ")");
68589ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        } else {
68599ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            sb.append("(1)");
686024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
68619ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki
68629ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
68639ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        // Accounts are valid by only checking one parameter, since we've
68649ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        // already ruled out partial accounts.
68659ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
68669ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        if (validAccount) {
68679ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            final Long accountId = mDbHelper.get().getAccountIdOrNull(accountWithDataSet);
68689ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            if (accountId == null) {
68699ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                // No such account.
68709ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                sb.setLength(0);
68719ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                sb.append("(1=2)");
68729ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            } else {
68739ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                sb.append(
68749ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                        " AND (" + Contacts._ID + " IN (" +
68759ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                        "SELECT " + RawContacts.CONTACT_ID + " FROM " + Tables.RAW_CONTACTS +
68769ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                        " WHERE " + RawContactsColumns.ACCOUNT_ID + "=" + accountId.toString() +
68779ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                        "))");
68789ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            }
68799ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        }
68809ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        qb.appendWhere(sb.toString());
688124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
688224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
6883f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
68849ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
6885e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6886e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6887e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
68889ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
6889e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
68909ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            String toAppend = "(" + RawContacts.ACCOUNT_NAME + "="
68919ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                    + DatabaseUtils.sqlEscapeString(accountWithDataSet.getAccountName()) + " AND "
68924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
68939ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                    + DatabaseUtils.sqlEscapeString(accountWithDataSet.getAccountType());
68949ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            if (accountWithDataSet.getDataSet() == null) {
6895f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + " IS NULL";
6896f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
6897f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + "=" +
68989ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                        DatabaseUtils.sqlEscapeString(accountWithDataSet.getDataSet());
689943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
69009ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            toAppend += ")";
690143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            qb.appendWhere(toAppend);
69024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
69034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
69044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
69054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
69064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
69079ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    private void appendAccountIdFromParameter(SQLiteQueryBuilder qb, Uri uri) {
69089ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
69099ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki
69109ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        // Accounts are valid by only checking one parameter, since we've
69119ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        // already ruled out partial accounts.
69129ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
69139ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        if (validAccount) {
69149ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            final Long accountId = mDbHelper.get().getAccountIdOrNull(accountWithDataSet);
69159ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            if (accountId == null) {
69169ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                // No such account.
69179ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                qb.appendWhere("(1=2)");
69189ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            } else {
69199ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                qb.appendWhere(
69209ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                        "(" + RawContactsColumns.ACCOUNT_ID + "=" + accountId.toString() + ")");
69219ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            }
69229ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        } else {
69239ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            qb.appendWhere("1");
69249ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        }
69259ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    }
69269ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki
69279d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    private AccountWithDataSet getAccountWithDataSetFromUri(Uri uri) {
6928f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6929f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
693043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6931e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6932e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6933e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6934e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
69355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6936fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6937e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
69389d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        return AccountWithDataSet.get(accountName, accountType, dataSet);
69399d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    }
69409d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
69419d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    private String appendAccountToSelection(Uri uri, String selection) {
69429d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
6943e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6944e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6945e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
69469d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
6947e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
69489d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "=");
69499d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            selectionSb.append(DatabaseUtils.sqlEscapeString(accountWithDataSet.getAccountName()));
69509d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            selectionSb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
69519d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            selectionSb.append(DatabaseUtils.sqlEscapeString(accountWithDataSet.getAccountType()));
69529d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            if (accountWithDataSet.getDataSet() == null) {
6953f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + " IS NULL");
6954f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
695543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + "=")
69569d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        .append(DatabaseUtils.sqlEscapeString(accountWithDataSet.getDataSet()));
69579d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            }
69589d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            if (!TextUtils.isEmpty(selection)) {
69599d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                selectionSb.append(" AND (");
69609d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                selectionSb.append(selection);
69619d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                selectionSb.append(')');
69629d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            }
69639d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            return selectionSb.toString();
69649d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        } else {
69659d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            return selection;
69669d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        }
69679d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    }
69689d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
69699d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki    private String appendAccountIdToSelection(Uri uri, String selection) {
69709d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final AccountWithDataSet accountWithDataSet = getAccountWithDataSetFromUri(uri);
69719d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
69729d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        // Accounts are valid by only checking one parameter, since we've
69739d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        // already ruled out partial accounts.
69749d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        final boolean validAccount = !TextUtils.isEmpty(accountWithDataSet.getAccountName());
69759d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki        if (validAccount) {
6976321b8325f2adc4b017237b4fe010a7f0f2b017adMakoto Onuki            final StringBuilder selectionSb = new StringBuilder();
6977321b8325f2adc4b017237b4fe010a7f0f2b017adMakoto Onuki
69789d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            final Long accountId = mDbHelper.get().getAccountIdOrNull(accountWithDataSet);
69799d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki            if (accountId == null) {
69809d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // No such account in the accounts table.  This means, there's no rows to be
69819d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                // selected.
6982321b8325f2adc4b017237b4fe010a7f0f2b017adMakoto Onuki                // Note even in this case, we still need to append the original selection, because
6983321b8325f2adc4b017237b4fe010a7f0f2b017adMakoto Onuki                // it may have query parameters.  If we remove these we'll get the # of parameters
6984321b8325f2adc4b017237b4fe010a7f0f2b017adMakoto Onuki                // mismatch exception.
6985321b8325f2adc4b017237b4fe010a7f0f2b017adMakoto Onuki                selectionSb.append("(1=2)");
6986321b8325f2adc4b017237b4fe010a7f0f2b017adMakoto Onuki            } else {
6987321b8325f2adc4b017237b4fe010a7f0f2b017adMakoto Onuki                selectionSb.append(RawContactsColumns.ACCOUNT_ID + "=");
6988321b8325f2adc4b017237b4fe010a7f0f2b017adMakoto Onuki                selectionSb.append(Long.toString(accountId));
698943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
69909d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki
6991e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
6992e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
6993e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
6994e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
6995e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
6996e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
6997e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
6998e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
6999e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
7000e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
7001e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
70027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
7003c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
7004c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
7005c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
7006c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
7007c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
7008f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
70092e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
7010c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
7011c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
7012c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
7013c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
7014c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
7015c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
7016c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
7017c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
7018c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
7019c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
7020c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
7021c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
7022c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
7023c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
7024c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
7025c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
7026c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
7027b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
7028f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
702947ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki        boolean success = false;
703047ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki        try {
703147ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            if (mode.equals("r")) {
703247ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki                waitForAccess(mReadAccessLatch);
703347ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            } else {
703447ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki                waitForAccess(mWriteAccessLatch);
703547ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            }
703647ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            final AssetFileDescriptor ret;
703747ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            if (mapsToProfileDb(uri)) {
703847ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki                switchToProfileMode();
703947ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki                ret = mProfileProvider.openAssetFile(uri, mode);
704047ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            } else {
704147ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki                switchToContactMode();
704247ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki                ret = openAssetFileLocal(uri, mode);
704347ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            }
704447ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            success = true;
704547ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            return ret;
704647ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki        } finally {
704747ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            if (VERBOSE_LOGGING) {
704847ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki                Log.v(TAG, "openAssetFile uri=" + uri + " mode=" + mode + " success=" + success);
704947ad37083874664d5983627c3ecd8e1c9e86a6f8Makoto Onuki            }
70505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
70515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
70525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
70535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public AssetFileDescriptor openAssetFileLocal(Uri uri, String mode)
70545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throws FileNotFoundException {
70555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
70565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
70575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
7058078f588cef389358adabc579de00747878f3c108Dave Santoro            if (mode.equals("r")) {
7059078f588cef389358adabc579de00747878f3c108Dave Santoro                mActiveDb.set(mContactsHelper.getReadableDatabase());
7060078f588cef389358adabc579de00747878f3c108Dave Santoro            } else {
7061078f588cef389358adabc579de00747878f3c108Dave Santoro                mActiveDb.set(mContactsHelper.getWritableDatabase());
7062078f588cef389358adabc579de00747878f3c108Dave Santoro            }
70635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
7064415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7065b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
7066b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
7067a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
7068bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
70695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
707024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
707124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
7072bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        new String[]{String.valueOf(contactId)});
7073e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
7074b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7075f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO: {
7076f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
7077f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
7078f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact ID can only be read.");
7079f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7080f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
70815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
7082f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{Contacts.PHOTO_FILE_ID},
7083f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
7084f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, null);
7085f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
708685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    if (c.moveToFirst()) {
708785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        long photoFileId = c.getLong(0);
708885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        return openDisplayPhotoForRead(photoFileId);
708985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    } else {
709085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        // No contact for this ID.
709185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        throw new FileNotFoundException(uri.toString());
709285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    }
709385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                } finally {
709485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    c.close();
709585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                }
709685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            }
709785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro
709885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            case PROFILE_DISPLAY_PHOTO: {
709985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                if (!mode.equals("r")) {
710085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    throw new IllegalArgumentException(
710185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                            "Display photos retrieved by contact ID can only be read.");
710285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                }
710385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
710485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        new String[]{Contacts.PHOTO_FILE_ID}, null, null, null, null, null);
710585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                try {
710685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    if (c.moveToFirst()) {
710785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        long photoFileId = c.getLong(0);
710885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        return openDisplayPhotoForRead(photoFileId);
710985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    } else {
711085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        // No profile record.
711185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        throw new FileNotFoundException(uri.toString());
711285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    }
7113f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
7114f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
7115f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7116f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7117f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7118bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
7119bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
7120f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
7121f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO: {
7122f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
7123f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
7124bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            "Photos retrieved by contact lookup key can only be read.");
7125f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7126f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                List<String> pathSegments = uri.getPathSegments();
7127f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                int segmentCount = pathSegments.size();
7128f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount < 4) {
71295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
7130f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Missing a lookup key", uri));
7131f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7132bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro
7133bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                boolean forDisplayPhoto = (match == CONTACTS_LOOKUP_ID_DISPLAY_PHOTO
7134bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        || match == CONTACTS_LOOKUP_DISPLAY_PHOTO);
7135f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String lookupKey = pathSegments.get(2);
7136bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                String[] projection = new String[]{Contacts.PHOTO_ID, Contacts.PHOTO_FILE_ID};
7137f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount == 5) {
7138f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long contactId = Long.parseLong(pathSegments.get(3));
7139f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
7140f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
71415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
7142f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            projection, null, null, null, null, null,
714315826b1a7fd6d7a5be9223a61d49c0b532ccf01eDaniel Lehmann                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey, null);
7144f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c != null) {
7145f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        try {
7146f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.moveToFirst();
7147bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            if (forDisplayPhoto) {
7148bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoFileId =
7149bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
7150bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openDisplayPhotoForRead(photoFileId);
7151bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            } else {
7152bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
7153bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
7154bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        Data._ID + "=?", new String[]{String.valueOf(photoId)});
7155bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            }
7156f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        } finally {
7157f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.close();
7158f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7159f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7160f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7161f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7162f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
7163f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
71645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
71655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection, Contacts._ID + "=?",
7166f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(contactId)}, null, null, null);
7167f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
7168f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
7169bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (forDisplayPhoto) {
7170bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
7171bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openDisplayPhotoForRead(photoFileId);
7172bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    } else {
7173bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
7174bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openPhotoAssetFile(mActiveDb.get(), uri, mode,
7175bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                Data._ID + "=?", new String[]{String.valueOf(photoId)});
7176bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
7177f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
7178f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
7179f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7180f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7181f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7182f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO: {
7183f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
7184f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                boolean writeable = !mode.equals("r");
7185f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7186f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Find the primary photo data record for this raw contact.
7187f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
7188f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
7189f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
71907cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
71915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection,
71927cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data.RAW_CONTACT_ID + "=? AND " + DataColumns.MIMETYPE_ID + "=?",
71937cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        new String[]{String.valueOf(rawContactId), String.valueOf(photoMimetypeId)},
7194f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, Data.IS_PRIMARY + " DESC");
7195f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long dataId = 0;
7196f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = 0;
7197f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
7198f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c.getCount() >= 1) {
7199f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        c.moveToFirst();
7200f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        dataId = c.getLong(0);
7201f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        photoFileId = c.getLong(1);
7202f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7203f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
7204f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
7205f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7206f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7207f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // If writeable, open a writeable file descriptor that we can monitor.
7208f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // When the caller finishes writing content, we'll process the photo and
7209f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // update the data record.
7210f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (writeable) {
7211f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForWrite(rawContactId, dataId, uri, mode);
7212f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } else {
7213f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
7214f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7215f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7216f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7217193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki            case DISPLAY_PHOTO_ID: {
7218f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = ContentUris.parseId(uri);
7219f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
7220f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
7221f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by key can only be read.");
7222f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7223f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return openDisplayPhotoForRead(photoFileId);
7224f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7225f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7226e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
722724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
72287cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
72295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
72307cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data._ID + "=? AND " + DataColumns.MIMETYPE_ID + "=" + photoMimetypeId,
723124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
7232d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7233d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7234fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
7235fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
7236fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
7237fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
7238fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7239fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
7240fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
7241fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
724242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
7243fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
724442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
724542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
724642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
724742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7248fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
7249f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
725042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
725142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
725242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
725342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
725442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
725542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
7256fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
725742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
7258fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
7259d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
7260d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
726142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
726242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
7263d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
726442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
7265d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
726642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
72675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // TODO: Figure out what to do if the profile contact is in the list.
72685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
726924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
727042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
727142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
727242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
727342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
7274d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7275d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
7276d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
7277d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
7278d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7279fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
7280f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
7281d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7282b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7283b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
72845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new FileNotFoundException(mDbHelper.get().exceptionMessage(
72855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        "File does not exist", uri));
7286b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
7287b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
7288b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7289afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private AssetFileDescriptor openPhotoAssetFile(SQLiteDatabase db, Uri uri, String mode,
7290afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            String selection, String[] selectionArgs)
7291e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
7292e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
72935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new FileNotFoundException(mDbHelper.get().exceptionMessage("Mode " + mode
7294e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
7295e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
7296e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7297e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
7298ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
7299e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
730008ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
7301f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7302f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
730308ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
730408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
730508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
730608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
7307e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
7308e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7309f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7310f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a display photo from the photo store for reading.
7311f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoFileId The display photo file ID
7312f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor that allows the file to be read.
7313f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @throws FileNotFoundException If no photo file for the given ID exists.
7314f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7315f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForRead(long photoFileId)
7316f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throws FileNotFoundException {
73175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        PhotoStore.Entry entry = mPhotoStore.get().get(photoFileId);
7318f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
7319d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            try {
7320d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                return makeAssetFileDescriptor(
7321d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        ParcelFileDescriptor.open(new File(entry.path),
7322d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                ParcelFileDescriptor.MODE_READ_ONLY),
7323d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        entry.size);
7324d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } catch (FileNotFoundException fnfe) {
7325d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
7326d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                throw fnfe;
7327d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            }
7328f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
7329f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
7330f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throw new FileNotFoundException("No photo file found for ID " + photoFileId);
7331f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7332f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7333f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7334f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7335f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a file descriptor for a photo to be written.  When the caller completes writing
7336f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to the file (closing the output stream), the image will be parsed out and processed.
7337f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If processing succeeds, the given raw contact ID's primary photo record will be
7338f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * populated with the inserted image (if no primary photo record exists, the data ID can
7339f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * be left as 0, and a new data record will be inserted).
7340f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rawContactId Raw contact ID this photo entry should be associated with.
7341f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param dataId Data ID for a photo mimetype that will be updated with the inserted
7342f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     image.  May be set to 0, in which case the inserted image will trigger creation
7343f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     of a new primary photo image data row for the raw contact.
7344f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param uri The URI being used to access this file.
7345f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param mode Read/write mode string.
7346f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor the caller can use to write an image file for the
7347f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     raw contact.
7348f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7349f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForWrite(long rawContactId, long dataId, Uri uri,
7350f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            String mode) {
7351f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
7352c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            ParcelFileDescriptor[] pipeFds = ParcelFileDescriptor.createPipe();
7353c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            PipeMonitor pipeMonitor = new PipeMonitor(rawContactId, dataId, pipeFds[0]);
7354c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            pipeMonitor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) null);
7355c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return new AssetFileDescriptor(pipeFds[1], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
7356f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } catch (IOException ioe) {
7357f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.e(TAG, "Could not create temp image file in mode " + mode);
7358f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return null;
7359f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7360f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7361f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7362f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7363c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * Async task that monitors the given file descriptor (the read end of a pipe) for
7364c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * the writer finishing.  If the data from the pipe contains a valid image, the image
7365c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * is either inserted into the given raw contact or updated in the given data row.
7366f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7367c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro    private class PipeMonitor extends AsyncTask<Object, Object, Object> {
7368c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private final ParcelFileDescriptor mDescriptor;
7369f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mRawContactId;
7370f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mDataId;
7371c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private PipeMonitor(long rawContactId, long dataId, ParcelFileDescriptor descriptor) {
7372f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mRawContactId = rawContactId;
7373f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mDataId = dataId;
7374c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            mDescriptor = descriptor;
7375f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7376f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7377f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        @Override
7378c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        protected Object doInBackground(Object... params) {
7379c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            AutoCloseInputStream is = new AutoCloseInputStream(mDescriptor);
7380f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
7381c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                Bitmap b = BitmapFactory.decodeStream(is);
7382f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (b != null) {
7383fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann                    waitForAccess(mWriteAccessLatch);
7384c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki                    PhotoProcessor processor = new PhotoProcessor(b, getMaxDisplayPhotoDim(),
7385c23a30e0510cf56d1dafddc79d1ab99ae9297a3fMakoto Onuki                            getMaxThumbnailDim());
7386f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7387f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Store the compressed photo in the photo store.
73885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PhotoStore photoStore = ContactsContract.isProfileId(mRawContactId)
73895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            ? mProfilePhotoStore
73905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            : mContactsPhotoStore;
73915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long photoFileId = photoStore.insert(processor);
7392f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7393c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // Depending on whether we already had a data row to attach the photo
7394c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // to, do an update or insert.
7395f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (mDataId != 0) {
7396f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Update the data record with the new photo.
7397f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues updateValues = new ContentValues();
7398f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7399f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7400f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7401f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7402f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7403f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            updateValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7404f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7405f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7406c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                        update(ContentUris.withAppendedId(Data.CONTENT_URI, mDataId),
7407c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                                updateValues, null, null);
7408f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    } else {
7409f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Insert a new primary data record with the photo.
7410f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues insertValues = new ContentValues();
7411f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7412f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7413f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7414f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7415f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7416f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.IS_PRIMARY, 1);
7417f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7418f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            insertValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7419f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7420f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7421f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insert(RawContacts.CONTENT_URI.buildUpon()
7422f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(String.valueOf(mRawContactId))
7423f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(RawContacts.Data.CONTENT_DIRECTORY).build(),
7424f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                insertValues);
7425f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7426c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro
7427f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7428c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            } catch (IOException e) {
7429c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                throw new RuntimeException(e);
7430f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7431c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return null;
7432f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7433f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7434f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7435d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
7436d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7437d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7438f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
7439d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
7440d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7441f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
7442d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
7443d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
7444d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7445d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
7446d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7447f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7448f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
7449f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
7450d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
7451ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
7452ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
7453d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7454d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7455d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7456f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
7457f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
7458f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7459f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7460f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
7461f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
7462f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7463f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7464d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7465d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
7466d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
7467d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
7468d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7469fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
7470fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
7471d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
7472dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
7473fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
7474fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
7475dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
7476dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
74777a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
7478dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
7479108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
74803711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        final Uri rawContactsUri;
74813711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        if (mapsToProfileDb(uri)) {
748282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Pre-authorize the URI, since the caller would have already gone through the
748382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // permission check to get here, but the pre-authorization at the top level wouldn't
748482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // carry over to the raw contact.
748582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            rawContactsUri = preAuthorizeUri(RawContactsEntity.PROFILE_CONTENT_URI);
74863711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        } else {
74873711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            rawContactsUri = RawContactsEntity.CONTENT_URI;
74883711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        }
7489108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
7490108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
74913711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null, rawContactsUri)) {
7492108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
7493108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
7494108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7495d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7496108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
7497108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
7498108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7499108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
7500108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
7501108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
7502108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
7503108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
7504108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
7505108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
7506108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
7507108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
7508108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
7509d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7510d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7511d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7512b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
75134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
75144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
7515415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7516415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
7517415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7518a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
75194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
7520b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
7521be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
75222d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
7523b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
7524b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
752524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
7526b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
7527f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
752842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
752924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
7530f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
7531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
7532bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
7533bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
7534f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO:
7535f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
7536f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
7537f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO:
7538193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki            case DISPLAY_PHOTO_ID:
7539f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return "image/jpeg";
7540b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
754124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
7542be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
7543b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
754424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
7545b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
7546f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
754724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
7548f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
7549508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
75505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = ContentUris.parseId(uri);
75515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
75525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mProfileHelper.getDataMimeType(id);
75535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                } else {
75545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mContactsHelper.getDataMimeType(id);
75555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
755648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
755748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
755848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
755948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
75609005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
75619005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
756248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
756348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
756448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
756548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
756648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
756748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
756848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
756948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
7570b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
7571b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
7572b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
7573b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
7574b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
7575b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
7576b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
7577b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
7578c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
7579c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
7580c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
7581c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
7582d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
7583d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
7584d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
7585d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
7586af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS:
7587af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_TYPE;
7588af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID:
7589af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_ITEM_TYPE;
7590af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS:
7591af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_TYPE;
7592af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS_ID:
7593af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_ITEM_TYPE;
7594af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_PHOTOS:
7595af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                throw new UnsupportedOperationException("Not supported for write-only URI " + uri);
759661efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
759761efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
75984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
75994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
76007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
760109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
760209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
760309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
760409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
760509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
760609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
760709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
760809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
760924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
761009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
761109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
76128727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
761324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
76148727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
76158727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
761609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
761709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
761824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
761909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
762009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
762109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
762209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
762324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
762424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
762509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
762609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
762709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
762809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
762909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
763009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
763109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
763209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
763309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
763424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
763509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
763609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
763709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
763809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
763909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
764009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
764109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
764209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
764309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
764409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
764509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
764609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
764709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
764809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
764909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
765009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
765109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
765209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
765309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
765409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
765509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
7656f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
7657f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7658f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
7659f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
7660f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7661f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7662f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7663f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
7664f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
76655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().insertNameLookup(rawContactId, dataId, lookupType, name);
7666f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7667f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7668f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7669f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
7670d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
7671f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7672f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
7673f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
76742d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
7675d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
7676d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
7677d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
7678d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
7679d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
7680d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
7681d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
7682e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
7683916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
7684916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
7685e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
7686e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
7687fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood    public boolean isPhoneNumber(String query) {
7688fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        if (TextUtils.isEmpty(query)) {
7689fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood            return false;
7690fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        }
7691fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        // assume a phone number if it has at least 1 digit
7692fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        return countPhoneNumberDigits(query) > 0;
7693fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood    }
7694fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood
7695fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood    /**
7696fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood     * Returns the number of digitis in a phone number ignoring special characters such as '-'.
7697fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood     * If the string is not a valid phone number, 0 is returned.
7698fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood     */
7699fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood    public static int countPhoneNumberDigits(String query) {
7700fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        int numDigits = 0;
7701fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        int len = query.length();
77029a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
7703fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood            char c = query.charAt(i);
7704fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood            if (Character.isDigit(c)) {
7705fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                numDigits ++;
7706fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood            } else if (c == '*' || c == '#' || c == 'N' || c == '.' || c == ';'
7707fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                    || c == '-' || c == '(' || c == ')' || c == ' ') {
7708fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                // carry on
7709fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood            } else if (c == '+' && numDigits == 0) {
7710fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                // plus before any digits is ok
7711fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood            } else {
7712fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                return 0; // not a phone number
77139a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
77149a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
7715fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        return numDigits;
77169a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
77179a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
77184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
77197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
77207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
77217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
77227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
77237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
77247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
77257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
77267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
77277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
77287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
7729f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
7730f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
77317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
77327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
77337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
77347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
77357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
77367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
77377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
77387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
77397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
77407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
77417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
77427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
77437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
77447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
77457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
77467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
77477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
77487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
77497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
77507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
77517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
77527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
77537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
77547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
77557a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
77567a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
77577a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
77587a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
77597a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
77607a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
77617a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
77627a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
77637a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
77647a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
77657a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
77667a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
77674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
77684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
77694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
7770b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
7771b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
7772b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
7773b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
7774b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
77754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
77764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
7777b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
7778b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
7779b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
7780caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
77815e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
77825e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
77835e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
77845e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
77855e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
77865e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
77875e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
77885e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
77895e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
77905e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
77915e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
7792caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
7793caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
7794caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
77955f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
7796caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
7797caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
7798caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
7799caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
78006f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
7801caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
78026f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
7803caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
7804f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
780573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
780643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Returns true if the specified account type and data set is writable.
780773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
780849ed71913609193a00059df944f6259e9397b0bdMakoto Onuki    public boolean isWritableAccountWithDataSet(String accountTypeAndDataSet) {
780943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (accountTypeAndDataSet == null) {
7810bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
7811bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
7812bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
781343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Boolean writable = mAccountWritability.get(accountTypeAndDataSet);
781473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
781573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
781673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
781773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
7818627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
7819627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
782043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // TODO(dsantoro): Need to update this logic to allow for sub-accounts.
7821627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
7822627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
782343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountTypeAndDataSet.equals(sync.accountType)) {
782473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
782573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
7826627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
7827627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
7828627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
7829627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
7830627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
783173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
783273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
783373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
783473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
783573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
783643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        mAccountWritability.put(accountTypeAndDataSet, writable);
783773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
7838627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
7839b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
7840d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
7841f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
7842f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
7843f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7844f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
7845f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7846f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7847f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7848f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7849f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7850f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
7851f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
7852f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7853f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7854f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7855f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
7856f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7857f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
7858f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
7859f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7860f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7861f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
7862f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
7863f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
7864f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
7865f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
7866f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7867f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7868f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
7869f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
7870f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
7871f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
7872f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7873f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7874f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
7875f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7876f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7877f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
7878f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
7879f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7880f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
7881f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
7882f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
7883f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
7884f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
7885f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
78865fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
78875fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
78885fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
78895fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
78905fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
78915fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
78925fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
78935fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
78945fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
78955fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
78965fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
7897f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7898f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7899f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
7900f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7901f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
7902f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
7903f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7904f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7905f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
7906f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
7907f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
7908f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7909f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7910f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7911f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
7912f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
7913f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
7914f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
7915f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
7916f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7917f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7918f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
7919f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
79205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
79210dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
79220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
79230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
79240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
79250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
79265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(mContactsHelper.getProperty(
79279ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki                DbProperties.AGGREGATION_ALGORITHM, "1"));
79280dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
79290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
79300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
7931bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
79320dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
79330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
7934565b62f354d8b6aadc760092a7dbf483f8bbbe17Makoto Onuki        final long start = SystemClock.elapsedRealtime();
79355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = null;
79360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
79375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
79385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db = mContactsHelper.getWritableDatabase();
79395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(db);
79405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.beginTransaction();
79415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = db.query(true,
79420dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
79430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
79440dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
79450dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
79469d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                    " AND r1." + RawContactsColumns.ACCOUNT_ID +
79479d990d339c9e3a9e03f6fe13c260d36665f00e61Makoto Onuki                        "=r2." + RawContactsColumns.ACCOUNT_ID,
79480dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
79490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
79500dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
79510dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
79520dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
79530dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
79540dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
79550dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
79560dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
79570dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
79580dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
79595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.aggregateInTransaction(mTransactionContext.get(), db);
7960bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
79615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
79629ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki            mContactsHelper.setProperty(DbProperties.AGGREGATION_ALGORITHM,
79630dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
79640dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
79655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (db != null) {
79665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.endTransaction();
79675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
7968565b62f354d8b6aadc760092a7dbf483f8bbbe17Makoto Onuki            final long end = SystemClock.elapsedRealtime();
79690dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
79700dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
79710dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
79720dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
79739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
7974193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki    @VisibleForTesting
79759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
7976193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki        if (!mIsPhoneInitialized) {
7977193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki            mIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
7978193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki            mIsPhoneInitialized = true;
79799a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
7980193f2da3b4c3e019cc3a85a7101478332b1869ecMakoto Onuki        return mIsPhone;
79819a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
798246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
7983fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood    boolean isVoiceCapable() {
7984fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        // this copied from com.android.phone.PhoneApp.onCreate():
7985fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood
7986fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        // "voice capable" flag.
7987fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        // This flag currently comes from a resource (which is
7988fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        // overrideable on a per-product basis):
7989fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        return getContext().getResources()
7990fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood                .getBoolean(com.android.internal.R.bool.config_voice_capable);
7991fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        // ...but this might eventually become a PackageManager "system
7992fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        // feature" instead, in which case we'd do something like:
7993fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        // return
7994fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood        //   getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);
7995fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood    }
7996fa5cdd337d4d696d326db03c68bfae8645c83b14Mathew Inwood
799746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
7998dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        final long currentTimeMillis = Clock.getInstance().currentTimeMillis();
799946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
800046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
8001dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        final ArrayList<Long> dataIds = new ArrayList<Long>(ids.length);
800246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
800346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
800446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
800546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
800646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
800746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
800846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
800946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
801046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
801146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
801246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
801346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
801446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
8015dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        final StringBuilder rawContactIdSelect = new StringBuilder();
8016dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        rawContactIdSelect.append("SELECT " + Data.RAW_CONTACT_ID + " FROM " + Tables.DATA +
8017dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                " WHERE " + Data._ID + " IN (");
8018dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        for (int i = 0; i < ids.length; i++) {
8019dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            if (i > 0) rawContactIdSelect.append(",");
8020dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            rawContactIdSelect.append(ids[i]);
802146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
8022dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        rawContactIdSelect.append(")");
802346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
8024dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        final SQLiteDatabase db = mActiveDb.get();
8025dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
8026dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        mSelectionArgs1[0] = String.valueOf(currentTimeMillis);
8027dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
8028dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        db.execSQL("UPDATE " + Tables.RAW_CONTACTS +
8029dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                " SET " + RawContacts.LAST_TIME_CONTACTED + "=?" +
8030dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                "," + RawContacts.TIMES_CONTACTED + "=" +
8031dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    "ifnull(" + RawContacts.TIMES_CONTACTED + ",0) + 1" +
8032dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                " WHERE " + RawContacts._ID + " IN (" + rawContactIdSelect.toString() + ")"
8033dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                , mSelectionArgs1);
8034dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        db.execSQL("UPDATE " + Tables.CONTACTS +
8035dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                " SET " + Contacts.LAST_TIME_CONTACTED + "=?" +
8036dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                "," + Contacts.TIMES_CONTACTED + "=" +
8037dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    "ifnull(" + Contacts.TIMES_CONTACTED + ",0) + 1" +
8038dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                " WHERE " + Contacts._ID + " IN (SELECT " + RawContacts.CONTACT_ID +
8039dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    " FROM " + Tables.RAW_CONTACTS +
8040dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    " WHERE " + RawContacts._ID + " IN (" + rawContactIdSelect.toString() + "))"
8041dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                , mSelectionArgs1);
804246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
804346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
804446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
8045dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki    private interface DataUsageStatQuery {
8046dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        String TABLE = Tables.DATA_USAGE_STAT;
8047dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
8048dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        String[] COLUMNS = new String[] {
8049dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                DataUsageStatColumns._ID,
8050dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        };
8051dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        int ID = 0;
8052dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
8053dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        String SELECTION = DataUsageStatColumns.DATA_ID + " =? AND "
8054dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
8055dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki    }
8056dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
805746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
805846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
805946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
806046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
806146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
8062f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    @VisibleForTesting
8063f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    /* package */ int updateDataUsageStat(
8064f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            List<Long> dataIds, String type, long currentTimeMillis) {
8065dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
8066dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        final SQLiteDatabase db = mActiveDb.get();
8067dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
8068dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        final String typeString = String.valueOf(getDataUsageFeedbackType(type, null));
8069dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        final String currentTimeMillisString = String.valueOf(currentTimeMillis);
8070dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
8071dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        for (long dataId : dataIds) {
8072dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            final String dataIdString = String.valueOf(dataId);
8073dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            mSelectionArgs2[0] = dataIdString;
8074dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            mSelectionArgs2[1] = typeString;
8075dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            final Cursor cursor = db.query(DataUsageStatQuery.TABLE,
8076dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    DataUsageStatQuery.COLUMNS, DataUsageStatQuery.SELECTION,
8077dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    mSelectionArgs2, null, null, null);
807846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
8079dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                if (cursor.moveToFirst()) {
8080dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    final long id = cursor.getLong(DataUsageStatQuery.ID);
8081dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
8082dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    mSelectionArgs2[0] = currentTimeMillisString;
8083dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    mSelectionArgs2[1] = String.valueOf(id);
8084dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
8085dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    db.execSQL("UPDATE " + Tables.DATA_USAGE_STAT +
8086dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                            " SET " + DataUsageStatColumns.TIMES_USED + "=" +
8087dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                                "ifnull(" + DataUsageStatColumns.TIMES_USED +",0)+1" +
8088dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                            "," + DataUsageStatColumns.LAST_TIME_USED + "=?" +
8089dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                            " WHERE " + DataUsageStatColumns._ID + "=?",
8090dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                            mSelectionArgs2);
8091dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                } else {
8092dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    mSelectionArgs4[0] = dataIdString;
8093dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    mSelectionArgs4[1] = typeString;
8094dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    mSelectionArgs4[2] = "1"; // times used
8095dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    mSelectionArgs4[3] = currentTimeMillisString;
8096dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                    db.execSQL("INSERT INTO " + Tables.DATA_USAGE_STAT +
8097dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                            "(" + DataUsageStatColumns.DATA_ID +
8098dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                            "," + DataUsageStatColumns.USAGE_TYPE_INT +
8099dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                            "," + DataUsageStatColumns.TIMES_USED +
8100dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                            "," + DataUsageStatColumns.LAST_TIME_USED +
8101dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                            ") VALUES (?,?,?,?)",
8102dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                            mSelectionArgs4);
810346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
810446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
8105dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki                cursor.close();
810646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
810746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
810846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
810946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
811046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
811146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
811246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
811346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
811446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
811546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
811646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
811746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
811846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
811946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
812046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
812146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
812246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
812346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
812446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
812546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
812646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
812746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
812846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
812946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
813046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
813146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
813246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
813346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
813446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
813546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
813646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
813746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
813846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
813946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
8140b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
8141b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
8142b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the URI for a deferred snippeting request
8143b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a deferred snippeting request is in the RI
8144b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
8145b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean deferredSnippetingRequested(Uri uri) {
8146b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        String deferredSnippeting =
8147b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            getQueryParameter(uri, SearchSnippetColumns.DEFERRED_SNIPPETING_KEY);
8148b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return !TextUtils.isEmpty(deferredSnippeting) &&  deferredSnippeting.equals("1");
8149b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
8150b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
8151b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
8152b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks if query is a single word or not.
8153b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if the query is one word or not
8154b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
8155b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean isSingleWordQuery(String query) {
8156b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return query.split(QUERY_TOKENIZER_REGEX).length == 1;
8157b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
8158b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
8159b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
8160b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the projection for a SNIPPET column indicating that a snippet is needed
8161b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a snippet is needed or not.
8162b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
8163b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean snippetNeeded(String [] projection) {
8164ac2a6e814edb3d5e5bcca28d7d3f3977a489c2edMakoto Onuki        return ContactsDatabaseHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET);
8165b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
81669ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki
81679ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    /**
8168084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki     * Create a single row cursor for a simple, informational queries, such as
8169084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki     * {@link ProviderStatus#CONTENT_URI}.
8170084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki     */
8171084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki    @VisibleForTesting
8172084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki    static Cursor buildSingleRowResult(String[] projection, String[] availableColumns,
8173084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki            Object[] data) {
8174084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki        Preconditions.checkArgument(availableColumns.length == data.length);
8175084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki        if (projection == null) {
8176084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki            projection = availableColumns;
8177084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki        }
8178084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki        final MatrixCursor c = new MatrixCursor(projection, 1);
8179084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki        final RowBuilder row = c.newRow();
8180084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki
8181084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki        // It's O(n^2), but it's okay because we only have a few columns.
8182084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki        for (int i = 0; i < c.getColumnCount(); i++) {
8183084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki            final String column = c.getColumnName(i);
8184084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki
8185084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki            boolean found = false;
8186084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki            for (int j = 0; j < availableColumns.length; j++) {
8187084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                if (availableColumns[j].equals(column)) {
8188084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                    row.add(data[j]);
8189084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                    found = true;
8190084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                    break;
8191084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                }
8192084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki            }
8193084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki            if (!found) {
8194084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki                throw new IllegalArgumentException("Invalid column " + projection[i]);
8195084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki            }
8196084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki        }
8197084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki        return c;
8198084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki    }
8199084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki
8200084fe28445cf74e3fa93522f8f8e5da6e065b8c3Makoto Onuki    /**
82019ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki     * @return the currently active {@link ContactsDatabaseHelper} for the current thread.
82029ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki     */
8203833072a525ea45426082d3bac0cd6ae1a8c36f22Makoto Onuki    @NeededForTesting
82049ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    public ContactsDatabaseHelper getThreadActiveDatabaseHelperForTest() {
82059ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki        return mDbHelper.get();
82069ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki    }
820735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
820835997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    @Override
820935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
821035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        pw.print("FastScrollingIndex stats:\n");
821135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        pw.printf("request=%d  miss=%d (%d%%)  avg time=%dms\n",
821235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                mFastScrollingIndexCacheRequestCount,
821335997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                mFastScrollingIndexCacheMissCount,
821435997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                safeDiv(mFastScrollingIndexCacheMissCount * 100,
821535997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                        mFastScrollingIndexCacheRequestCount),
821635997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                safeDiv(mTotalTimeFastScrollingIndexGenerate, mFastScrollingIndexCacheMissCount)
821735997f3fdee2984b6d5373326110eda26929001aMakoto Onuki                );
821835997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    }
821935997f3fdee2984b6d5373326110eda26929001aMakoto Onuki
822035997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    private static final long safeDiv(long dividend, long divisor) {
822135997f3fdee2984b6d5373326110eda26929001aMakoto Onuki        return (divisor == 0) ? 0 : dividend / divisor;
822235997f3fdee2984b6d5373326110eda26929001aMakoto Onuki    }
8223dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki
8224dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki    private static final int getDataUsageFeedbackType(String type, Integer defaultType) {
8225dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        if (DataUsageFeedback.USAGE_TYPE_CALL.equals(type)) {
8226dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            return DataUsageStatColumns.USAGE_TYPE_INT_CALL; // 0
8227dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        }
8228dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        if (DataUsageFeedback.USAGE_TYPE_LONG_TEXT.equals(type)) {
8229dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            return DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT; // 1
8230dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        }
8231dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        if (DataUsageFeedback.USAGE_TYPE_SHORT_TEXT.equals(type)) {
8232dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            return DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT; // 2
8233dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        }
8234dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        if (defaultType != null) {
8235dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki            return defaultType;
8236dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        }
8237dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki        throw new IllegalArgumentException("Invalid usage type " + type);
8238dfab50ecd585e55769dea451cb3a47ff69b8b86dMakoto Onuki    }
82394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
8240