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
1953214b3ed12b0ff9cb589b6559311f2ac142f2e3Bjorn Bringertimport com.android.common.content.SyncStateContentProviderHelper;
205b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport com.android.providers.contacts.ContactAggregator.AggregationSuggestionParameter;
2197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
2297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
2397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
2597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
2697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
2797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
2871340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
2997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
3023ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.Joins;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
351dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport com.android.providers.contacts.ContactsDatabaseHelper.PhotoFilesColumns;
3697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
3803197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
3997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
4097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.StreamItemPhotosColumns;
42f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport com.android.providers.contacts.ContactsDatabaseHelper.StreamItemsColumns;
4397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
44ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmannimport com.android.providers.contacts.ContactsDatabaseHelper.Views;
45d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmannimport com.android.providers.contacts.SearchIndexManager.FtsQueryBuilder;
462f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawaimport com.android.providers.contacts.util.DbQueryUtils;
4797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardComposer;
4897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardConfig;
4997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
5097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
5197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
52f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawaimport com.google.common.annotations.VisibleForTesting;
5397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
54b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
55caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
565b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
57bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.Notification;
58bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.NotificationManager;
59bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.PendingIntent;
60c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
61568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
62568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
636ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
6435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
6567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
6667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
67627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
68bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.Intent;
69568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
71627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
7267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
730bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.pm.PackageManager;
740bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.pm.PackageManager.NameNotFoundException;
755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoroimport android.content.pm.ProviderInfo;
76f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.content.res.Resources;
780bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.res.Resources.NotFoundException;
79409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onukiimport android.database.AbstractCursor;
80e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CrossProcessCursor;
814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
82e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CursorWindow;
83ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
84ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
8509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
8609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
8808ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
90f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.Bitmap;
91f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.BitmapFactory;
924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
93d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
94c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.AsyncTask;
95bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Binder;
966ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
97bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Handler;
98bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.HandlerThread;
99bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Message;
100ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
101c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.ParcelFileDescriptor.AutoCloseInputStream;
102bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Process;
103b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
10415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.os.StrictMode;
1050dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
1060e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
1073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
108508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
1093de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
110b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
11182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoroimport android.provider.ContactsContract.Authorization;
11297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
11397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
11497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
11597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
1166d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Note;
11797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
11897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
11997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
1204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
12197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
12297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
123ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
1243de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1255b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1263de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
12771340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport android.provider.ContactsContract.DataUsageFeedback;
128d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
129f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.DisplayPhoto;
1303de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
131bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1323de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
1331dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport android.provider.ContactsContract.PhotoFiles;
1340c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoroimport android.provider.ContactsContract.Profile;
13509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1363de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
1373711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenenimport android.provider.ContactsContract.RawContactsEntity;
138916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1393de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
14082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
1413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.provider.ContactsContract.StreamItemPhotos;
142f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.StreamItems;
14397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
14497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
145a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
1469a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikovimport android.telephony.TelephonyManager;
147a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
148c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
150108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.BufferedWriter;
151d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
152f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.File;
153b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
154d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
155d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
156108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.OutputStreamWriter;
157108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawaimport java.io.Writer;
158d0eb93009559d095de0448907527aeb059801dc4Dave Santoroimport java.security.SecureRandom;
15942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
16146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawaimport java.util.Arrays;
1625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
16342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
164b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1650e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
167622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
168b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1690e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
170ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
176078f588cef389358adabc579de00747878f3c108Dave Santoropublic class ContactsProvider2 extends AbstractContactsProvider
177078f588cef389358adabc579de00747878f3c108Dave Santoro        implements OnAccountsUpdateListener {
178caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
179bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
180bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
181bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
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_IMPORT_LEGACY_CONTACTS = 2;
18615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_ACCOUNTS = 3;
18715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_LOCALE = 4;
18815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private static final int BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM = 5;
18905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_SEARCH_INDEX = 6;
19005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_PROVIDER_STATUS = 7;
19105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_UPDATE_DIRECTORIES = 8;
19205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    private static final int BACKGROUND_TASK_CHANGE_LOCALE = 9;
193f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int BACKGROUND_TASK_CLEANUP_PHOTOS = 10;
194619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Limit for the maximum number of social stream items to store under a raw contact. */
1993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int MAX_STREAM_ITEMS_PER_RAW_CONTACT = 5;
2003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
201f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** Rate limit (in ms) for photo cleanup.  Do it at most once per day. */
202f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_CLEANUP_RATE_LIMIT = 24 * 60 * 60 * 1000;
203f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
2043d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
20582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Default expiration duration for pre-authorized URIs.  May be overridden from a secure
20682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * setting.
20782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
20882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private static final int DEFAULT_PREAUTHORIZED_URI_EXPIRATION = 5 * 60 * 1000;
20982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
21082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
21182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Random URI parameter that will be appended to preauthorized URIs for uniqueness.
21282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
21382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private static final String PREAUTHORIZED_URI_TOKEN = "perm_token";
21482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
21582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
216b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
2173d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
2183d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
2193d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
220b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
221b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
22251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
2233d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
2250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
2260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
2270e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
2280e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
2295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final ProfileAwareUriMatcher sUriMatcher =
2305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ProfileAwareUriMatcher(UriMatcher.NO_MATCH);
2314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2322f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
2332f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * Used to insert a column into strequent results, which enables SQL to sort the list using
2342f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     * the total times contacted. See also {@link #sStrequentFrequentProjectionMap}.
2352f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
2362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private static final String TIMES_USED_SORT_COLUMN = "times_used_sort";
2375e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
23845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final String FREQUENT_ORDER_BY = DataUsageStatColumns.TIMES_USED + " DESC,"
23945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
24045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
2416e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
2429b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
2439b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2449b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
2459b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2466e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2479b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
2489b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2499b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
2509b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
251de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
252de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2533716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2543716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2553716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
257d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
258d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
261a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
266a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
267bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_PHOTO = 1010;
268bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_PHOTO = 1011;
269bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_DISPLAY_PHOTO = 1012;
270bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DISPLAY_PHOTO = 1013;
271bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DISPLAY_PHOTO = 1014;
272bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_VCARD = 1015;
273bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_MULTI_VCARD = 1016;
274bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DATA = 1017;
275bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DATA = 1018;
276bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_ENTITIES = 1019;
277bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ENTITIES = 1020;
278bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1021;
279bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_STREAM_ITEMS = 1022;
280bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_STREAM_ITEMS = 1023;
281bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_STREAM_ITEMS = 1024;
282bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_FREQUENT = 1025;
2834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
28746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
288f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_DISPLAY_PHOTO = 2006;
289f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2007;
29082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS_ID = 2008;
2914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
294ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
29548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
29648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
29748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
29848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
29948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
30048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
30148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
30248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
303a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3046bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
3056bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
306b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
307b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
308b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
30982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
31082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
3111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
31231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
31331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
314eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
315eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
316ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
317ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
318ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
319ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
32035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
321b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
3225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE = 11002;
3235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE_ID = 11003;
32435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
325c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
326c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
327c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
32846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
32946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
33009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
33109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
332d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
333d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
334d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
3367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
33724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
33824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
33924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
34024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
34124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
34224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
34324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
34424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
34524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
3465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_STATUS_UPDATES = 19009;
3473202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro    private static final int PROFILE_RAW_CONTACT_ENTITIES = 19010;
34885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro    private static final int PROFILE_PHOTO = 19011;
34985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro    private static final int PROFILE_DISPLAY_PHOTO = 19012;
35024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
35146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
35246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
3533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS = 21000;
3543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_PHOTOS = 21001;
3553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID = 21002;
3563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS = 21003;
3573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
3583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_LIMIT = 21005;
3593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
360f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int DISPLAY_PHOTO = 22000;
361f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_DIMENSIONS = 22001;
362f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
3635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Inserts into URIs in this map will direct to the profile database if the parent record's
3645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // value (looked up from the ContentValues object with the key specified by the value in this
3655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // map) is in the profile ID-space (see {@link ProfileDatabaseHelper#PROFILE_ID_SPACE}).
3665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final Map<Integer, String> INSERT_URI_ID_VALUE_MAP = Maps.newHashMap();
3675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    static {
3685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(DATA, Data.RAW_CONTACT_ID);
3695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_DATA, Data.RAW_CONTACT_ID);
3705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STATUS_UPDATES, StatusUpdates.DATA_ID);
3715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_ID_STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_ID_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
3765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
37736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // Any interactions that involve these URIs will also require the calling package to have either
37836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // android.permission.READ_SOCIAL_STREAM permission or android.permission.WRITE_SOCIAL_STREAM
37936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // permission, depending on the type of operation being performed.
38036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private static final List<Integer> SOCIAL_STREAM_URIS = Lists.newArrayList(
38136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_ID_STREAM_ITEMS,
38236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_LOOKUP_STREAM_ITEMS,
38336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_LOOKUP_ID_STREAM_ITEMS,
38436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            RAW_CONTACTS_ID_STREAM_ITEMS,
38536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            RAW_CONTACTS_ID_STREAM_ITEMS_ID,
38636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS,
38736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_PHOTOS,
38836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID,
38936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID_PHOTOS,
39036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID_PHOTOS_ID
39136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    );
39236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
393dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
394dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
395dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
396dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
397dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
39843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
39943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET
40043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + "=" + RawContactsColumns.CONCRETE_DATA_SET + " OR "
40143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
40243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
403dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " 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 "
407dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
408dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
409dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
41043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
41143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + "="
41243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " OR "
41343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
41443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
41543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + " AND " + Groups.AUTO_ADD + " != 0";
416dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
417dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
418dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
419dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
420dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
421dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
422dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
423dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
424dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
425dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
426dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
427dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
428d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
429f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
430f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
431f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
43267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
43367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
4346cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
4356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_TYPE,
4366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_NAME,
43743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContactsColumns.CONCRETE_DATA_SET,
4383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
439f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
440ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
441ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
442d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
4436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_TYPE = 1;
4446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_NAME = 2;
44543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_SET = 3;
44643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_ID = 4;
44743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int CONTACT_ID = 5;
448ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
4491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
450f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
45119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
45219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
45319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
454ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
455ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
456ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
45743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.DATA_SET,
45819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
45919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
46019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
461ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
462ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
46343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int DATA_SET = 3;
46419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
46519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
466c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
467caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
46871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
46971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
47071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
47171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
47271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
47371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
47471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
47571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
4767cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
4777cf50494501938f175d288077145acf49da8f171Daniel Lehmann                                    + " AND " + GroupMembership.GROUP_ROW_ID + "="
47871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
47971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
48071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
48171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
482a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
483a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
484a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
485a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
486a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
487a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
488a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
489a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
490a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
491a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
492a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
493a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
494c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
495c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
496c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
497c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
498c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
499c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
500f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    private static final String TIME_SINCE_LAST_USED =
501f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            "(strftime('%s', 'now') - " + DataUsageStatColumns.LAST_TIME_USED + "/1000)";
502f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa
503c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
504c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
5052262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * second in_visible_group, then the rest.
5062262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
5072262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly
508c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
50946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
51046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
511c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
512c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
5132262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
5142262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
515f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa        + "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
51646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
517f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa                + " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
51846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
51946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
52046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
52146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
52246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
523c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
524c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
52546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
52646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
52746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
528c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
529916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
530916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
531916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
532916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
53392ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
534916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
535f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
536f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
537f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
538f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
539f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
540f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
541f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
542f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
543f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
544f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
54543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.DATA_SET,
54643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
547f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
548f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
549f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
550f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
551f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
552916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
564f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            .add(Contacts.PHOTO_FILE_ID)
5653d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
5663d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
572cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
59303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
59943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.DATA_SET)
60043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.ACCOUNT_TYPE_AND_DATA_SET)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
669038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
674e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
67924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
684916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
689916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6905e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6932f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
6982f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7034928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. Right now Starred part just returns NULL for
7044928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * those data columns (frequent part should return real ones in data table).
7054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyStarredProjectionMap
7074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
7104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER, "NULL")
7114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE, "NULL")
7124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL, "NULL")
7134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
7154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7164928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7174928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. We hard-code {@link Contacts#IS_USER_PROFILE} to NULL,
7184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * because sContactsProjectionMap specifies a field that doesn't exist in the view behind the
7194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * query that uses this projection map.
7204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyFrequentProjectionMap
7224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, DataUsageStatColumns.CONCRETE_TIMES_USED)
7254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER)
7264928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE)
7274928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL)
7284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Contacts.IS_USER_PROFILE, "NULL")
7294928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7304928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
731f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
733fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
738ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
75624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
761a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
76824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
774a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
78224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
79158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    /** Contains columns in PhoneLookup which are not contained in the data view. */
79258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private static final ProjectionMap sSipLookupColumns = ProjectionMap.builder()
79358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.NUMBER, SipAddress.SIP_ADDRESS)
79458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.TYPE, "0")
79558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.LABEL, "NULL")
79658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .add(PhoneLookup.NORMALIZED_NUMBER, "NULL")
79758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .build();
79858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
7994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
80524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
809f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
81358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    /** Contains columns from the data view used for SIP address lookup. */
81458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private static final ProjectionMap sDataSipLookupProjectionMap = ProjectionMap.builder()
81558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sDataProjectionMap)
81658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sSipLookupColumns)
81758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .build();
81858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
8195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
821f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
82324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
824f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
825f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
826f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
827f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
828f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
829f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
83058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    /** Contains columns from the data view used for SIP address lookup. */
83158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private static final ProjectionMap sDistinctDataSipLookupProjectionMap = ProjectionMap.builder()
83258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sDistinctDataProjectionMap)
83358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .addAll(sSipLookupColumns)
83458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            .build();
83558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
8369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
838f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
839f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
840f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
841f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
844f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
845f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
8463d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
8473d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
848f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
849f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
850f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
851f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
852f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
853f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
8542530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
855f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
856f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
857ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
858f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
859f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
860f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
861f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
86243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.DATA_SET)
86343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.ACCOUNT_TYPE_AND_DATA_SET)
864f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
865f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
866f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
867f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
868f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
869f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
870f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
871f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
872f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
873f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
874f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
875f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
876f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
877c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
878f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
879f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
880f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
881f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
882f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
883f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
88423ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki    /**
88523ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * Contains {@link Groups} columns along with summary details.
88623ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     *
88723ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * Note {@link Groups#SUMMARY_COUNT} doesn't exist in groups/view_groups.
88823ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * When we detect this column being requested, we join {@link Joins#GROUP_MEMBER_COUNT} to
88923ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki     * generate it.
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 + ")")
898f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .build();
899f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa
900f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // This is only exposed as hidden API for the contacts app, so we can be very specific in
901f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // the filtering
902f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    private static final ProjectionMap sGroupsSummaryProjectionMapWithGroupCountPerAccount =
903f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            ProjectionMap.builder()
904f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .addAll(sGroupsSummaryProjectionMap)
905f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
906f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(*) FROM " + Views.GROUPS + " WHERE "
907f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + "(" + Groups.ACCOUNT_NAME + "="
908f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + GroupsColumns.CONCRETE_ACCOUNT_NAME
909f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
910f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_TYPE + "=" + GroupsColumns.CONCRETE_ACCOUNT_TYPE
911f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
912f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.DELETED + "=0 AND "
913f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.FAVORITES + "=0 AND "
914f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.AUTO_ADD + "=0"
915f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")"
916f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " GROUP BY "
917f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_NAME + ", " + Groups.ACCOUNT_TYPE
918f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                   + ")")
919f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
920f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
921373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
922f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
923f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
924f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
925f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
926f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
927f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
928f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
929eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
930f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
931f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
932f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
933f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            .add(Settings.DATA_SET)
934f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
935f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
936f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
937f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
938f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
939f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
940f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
941f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
942f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
943f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
944f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
945f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
946f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
947f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE
948f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                + " AND ((" + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
949f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + " IS NULL) OR ("
950f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + GroupsColumns.CONCRETE_DATA_SET + "="
951f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + "))))=0"
952f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
953f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
954f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
955f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
956f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
957f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
958f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
959f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
960f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
961f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
962f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
963f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
964f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
965f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
966f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
967f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
968f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
969f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
970f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
971f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
97282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
973f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
974f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
975f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
976f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
977f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
978f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
979f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
980f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
981f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
982f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
983f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
984f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
985f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
986f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
987f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
988f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
989f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
990f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
991f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
992f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
993f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
9943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
9953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
9969b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems._ID)
9979b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.CONTACT_ID)
998af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann            .add(StreamItems.CONTACT_LOOKUP_KEY)
9999b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_NAME)
10009b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_TYPE)
10019b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.DATA_SET)
10023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
10039b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID)
10043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
10053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
10063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
10073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
10083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
10093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
10100bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC1)
10110bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC2)
10120bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC3)
10130bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC4)
10143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
10153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
10163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
10173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
10183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
10190bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID, RawContactsColumns.CONCRETE_SOURCE_ID)
10203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
10213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
10226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_FILE_ID)
10236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_URI,
10246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    "'" + DisplayPhoto.CONTENT_URI + "'||'/'||" + StreamItemPhotos.PHOTO_FILE_ID)
10251dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.HEIGHT)
10261dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.WIDTH)
10271dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.FILESIZE)
10280bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC1)
10290bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC2)
10300bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC3)
10310bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC4)
10323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
10333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1034d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
1035f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
1036f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
1037f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
1038f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
1039f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
1040f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
1041f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
1042f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
1043f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
1044778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
1045778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
1046f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
10477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
10499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
10509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
10519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
10529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
10539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
10542526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
10552526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1056bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1057bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
1058bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1059bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
106051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
106103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
106203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
106303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
106403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
106503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
10669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
10679a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
10689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
1069f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
10701129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
10711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
10722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
10732526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1074f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
1075f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
107646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
107746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
107846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
107946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
108046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
108146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
10824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
10834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
1084a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
1085d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
1086d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
1087a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
1088a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
10893653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
10903653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
10912d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
10922d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
1093a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
1094f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
1095f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_ID_DISPLAY_PHOTO);
10963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
10973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
1098c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
10995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
11005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
11012149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
1102bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/photo",
1103bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_PHOTO);
11045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
11052149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
11062149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
1107bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/photo",
1108bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_ID_PHOTO);
1109f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
1110f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_DISPLAY_PHOTO);
1111f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
1112f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
1113a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
1114a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
1115a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
1116a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
11173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
11183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
11193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
11203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
1121f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
112242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
112342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
11245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
1125ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
1126ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
11275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
112845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
11293653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
11305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
11315ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
11325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
1133f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
1134f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                RAW_CONTACTS_ID_DISPLAY_PHOTO);
113546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
11363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
11373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
113882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items/#",
113982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                RAW_CONTACTS_ID_STREAM_ITEMS_ID);
114046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
114146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
1142b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
11434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
11444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
1145ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
114648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
11475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
1148ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
11494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
115048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
11511dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
11525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
11535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
11544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
1155ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
115648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
115746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
115846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
11591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1160ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1161ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1162ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1163ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
116435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1165b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1166b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
11675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/" + SyncStateContentProviderHelper.PATH,
11685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE);
11695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY,
11705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                "profile/" + SyncStateContentProviderHelper.PATH + "/#",
11715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE_ID);
117235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1173a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1174b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1175b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1176b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1177b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
11784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1179eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1180eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
118182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
118282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
11831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1184c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1185c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1186c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1187c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
11882d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1189c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1190c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
119109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1192d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1193d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1194d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
11957a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
11967a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
119724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
119824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
119924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
120024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
120124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
120285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/photo", PROFILE_PHOTO);
120385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/display_photo", PROFILE_DISPLAY_PHOTO);
120424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
120524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
120624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
120724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
120824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
120924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
121024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
121124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
12125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/status_updates",
12135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_STATUS_UPDATES);
12143202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contact_entities",
12153202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro                PROFILE_RAW_CONTACT_ENTITIES);
121646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
12173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
12183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
12193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
12203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
12213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
12223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
12233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
12243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
12255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", DISPLAY_PHOTO);
1226f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
1227f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
122846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
122946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
123046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
123146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
123246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
123346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
123446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
123519a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
123619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1237d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1238d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1239d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1240d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1241d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1242d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1243d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1244d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1245d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
12464458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
12474458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1248d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
12493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
125043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * An entry in group id cache. It maps the combination of (account type, account name, data set,
1251ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1252ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1253e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1254ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1255ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
125643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet;
1257ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1258ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1259ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1260a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1261e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1262e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1263e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1264e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1265e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
126624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
1267f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of display photos.  Larger images will be scaled
1268f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to fit.
1269f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1270f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxDisplayPhotoDim;
1271f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1272f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1273f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of photo thumbnails.
1274f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1275f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxThumbnailPhotoDim;
1276f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
12785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Sub-provider for handling profile requests against the profile database.
12795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
12805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileProvider mProfileProvider;
1281f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12824097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1283f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1284315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1285622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1286622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
128772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
12885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1289078f588cef389358adabc579de00747878f3c108Dave Santoro    // The database tag to use for representing the contacts DB in contacts transactions.
1290078f588cef389358adabc579de00747878f3c108Dave Santoro    /* package */ static final String CONTACTS_DB_TAG = "contacts";
1291078f588cef389358adabc579de00747878f3c108Dave Santoro
1292078f588cef389358adabc579de00747878f3c108Dave Santoro    // The database tag to use for representing the profile DB in contacts transactions.
1293078f588cef389358adabc579de00747878f3c108Dave Santoro    /* package */ static final String PROFILE_DB_TAG = "profile";
1294078f588cef389358adabc579de00747878f3c108Dave Santoro
12955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
12965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * The active (thread-local) database.  This will be switched between a contacts-specific
12975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database and a profile-specific database, depending on what the current operation is
12985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * targeted to.
12995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
13005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<SQLiteDatabase> mActiveDb = new ThreadLocal<SQLiteDatabase>();
13015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13026efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    /**
13036efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * The thread-local holder of the active transaction.  Shared between this and the profile
13046efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * provider, to keep transactions on both databases synchronized.
13056efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     */
13066efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    private final ThreadLocal<ContactsTransaction> mTransactionHolder =
13076efb7db26598b105342d02207e0ca1c8725c10daDave Santoro            new ThreadLocal<ContactsTransaction>();
13086efb7db26598b105342d02207e0ca1c8725c10daDave Santoro
13095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // This variable keeps track of whether the current operation is intended for the profile DB.
13105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<Boolean> mInProfileMode = new ThreadLocal<Boolean>();
13115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Separate data row handler instances for contact data and profile data.
13135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mDataRowHandlers;
13145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mProfileDataRowHandlers;
13155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile, we will use one of two
13175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // database helper instances.
13185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactsDatabaseHelper> mDbHelper =
13195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<ContactsDatabaseHelper>();
13205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactsDatabaseHelper mContactsHelper;
13215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileDatabaseHelper mProfileHelper;
13225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
13245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two aggregator instances.
13255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactAggregator> mAggregator = new ThreadLocal<ContactAggregator>();
1326622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
13275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactAggregator mProfileAggregator;
13285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
13305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two photo store instances (with their files stored in separate subdirectories).
13315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<PhotoStore> mPhotoStore = new ThreadLocal<PhotoStore>();
13325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mContactsPhotoStore;
13335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mProfilePhotoStore;
13345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // The active transaction context will switch depending on the operation being performed.
13365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Both transaction contexts will be cleared out when a batch transaction is started, and
13375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // each will be processed separately when a batch transaction completes.
13385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private TransactionContext mContactTransactionContext = new TransactionContext(false);
13395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private TransactionContext mProfileTransactionContext = new TransactionContext(true);
13405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<TransactionContext> mTransactionContext =
13415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<TransactionContext>();
13425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
134382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    // Duration in milliseconds that pre-authorized URIs will remain valid.
134482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private long mPreAuthorizedUriDuration;
134582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
134682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    // Map of single-use pre-authorized URIs to expiration times.
134782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private Map<Uri, Long> mPreAuthorizedUris = Maps.newHashMap();
134882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
1349d0eb93009559d095de0448907527aeb059801dc4Dave Santoro    // Random number generator.
1350d0eb93009559d095de0448907527aeb059801dc4Dave Santoro    private SecureRandom mRandom = new SecureRandom();
135182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
1352f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1353a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1354d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1355f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1356a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
135720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
135873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
135920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
136009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
13613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
136209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
136315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
136415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
136515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1366bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
136773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
13681a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
13691a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
137081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
137181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
13724cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
13733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1374d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1375bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1376bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1377bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1378f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mLastPhotoCleanup = 0;
1379f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
13804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
13814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1382663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1383663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate start");
1384663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        }
1385de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1386ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1387ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1388ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1389ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1390ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1391663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        } finally {
1392663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1393663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki                Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate finish");
1394663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            }
1395ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1396ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
139735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1398ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
139915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
140015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
140115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
14023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Resources resources = getContext().getResources();
1403f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxDisplayPhotoDim = resources.getInteger(
1404f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_display_photo_dim);
1405f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxThumbnailPhotoDim = resources.getInteger(
1406f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_thumbnail_photo_dim);
14073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1408078f588cef389358adabc579de00747878f3c108Dave Santoro        mContactsHelper = getDatabaseHelper(getContext());
14095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
1410078f588cef389358adabc579de00747878f3c108Dave Santoro
1411078f588cef389358adabc579de00747878f3c108Dave Santoro        // Set up the DB helper for keeping transactions serialized.
1412078f588cef389358adabc579de00747878f3c108Dave Santoro        setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG);
1413078f588cef389358adabc579de00747878f3c108Dave Santoro
141472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1415a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
141665ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1417bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
141815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
141915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
142072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1421bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1422bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1423bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1424bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1425bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1426bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1427bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1428bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1429bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
14302a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
14315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set up the sub-provider for handling profiles.
14325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider = getProfileProvider();
1433c990980ab4beb7b81c3337526f1bdcd5d1a14730Dave Santoro        mProfileProvider.setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG);
14345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        ProviderInfo profileInfo = new ProviderInfo();
14355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.readPermission = "android.permission.READ_PROFILE";
14365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.writePermission = "android.permission.WRITE_PROFILE";
14375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider.attachInfo(getContext(), profileInfo);
1438078f588cef389358adabc579de00747878f3c108Dave Santoro        mProfileHelper = mProfileProvider.getDatabaseHelper(getContext());
14395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
144082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        // Initialize the pre-authorized URI duration.
144182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        mPreAuthorizedUriDuration = android.provider.Settings.Secure.getLong(
144282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                getContext().getContentResolver(),
144382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                android.provider.Settings.Secure.CONTACTS_PREAUTH_URI_EXPIRATION,
144482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                DEFAULT_PREAUTHORIZED_URI_EXPIRATION);
144582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
144615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1447bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1448bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1449bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1450bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
145105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1452bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
145315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
1454f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
14553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
145649d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
14574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
14584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1459767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
146051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
146151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
146204b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
146315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
14645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mLegacyApiSupport = new LegacyApiSupport(context, mContactsHelper, this,
14655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mGlobalSearchSupport);
14664cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
14675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mNameSplitter = mContactsHelper.createNameSplitter();
14684cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
14694cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
14705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mCommonNicknameCache = new CommonNicknameCache(mContactsHelper.getReadableDatabase());
1471cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
14725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactAggregator = new ContactAggregator(this, mContactsHelper,
147315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14745b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
14755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator = new ProfileAggregator(this, mProfileHelper,
14765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1478f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
14795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore = new PhotoStore(getContext().getFilesDir(), mContactsHelper);
14815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore = new PhotoStore(new File(getContext().getFilesDir(), "profile"),
14825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileHelper);
14835b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1484bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
14855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mDataRowHandlers, mContactsHelper, mContactAggregator,
14865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsPhotoStore);
14875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileDataRowHandlers = new HashMap<String, DataRowHandler>();
14885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mProfileDataRowHandlers, mProfileHelper, mProfileAggregator,
14895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfilePhotoStore);
14905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set initial thread-local state variables for the Contacts DB.
14925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
14935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
1494bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
14955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private void initDataRowHandlers(Map<String, DataRowHandler> handlerMap,
14965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            ContactsDatabaseHelper dbHelper, ContactAggregator contactAggregator,
14975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            PhotoStore photoStore) {
14985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Context context = getContext();
14995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Email.CONTENT_ITEM_TYPE,
15005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForEmail(context, dbHelper, contactAggregator));
15015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Im.CONTENT_ITEM_TYPE,
15025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForIm(context, dbHelper, contactAggregator));
15035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Organization.CONTENT_ITEM_TYPE,
15045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForOrganization(context, dbHelper, contactAggregator));
15055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Phone.CONTENT_ITEM_TYPE,
15065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoneNumber(context, dbHelper, contactAggregator));
15075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Nickname.CONTENT_ITEM_TYPE,
15085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNickname(context, dbHelper, contactAggregator));
15095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredName.CONTENT_ITEM_TYPE,
15105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredName(context, dbHelper, contactAggregator,
1511bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
15125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredPostal.CONTENT_ITEM_TYPE,
15135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredPostal(context, dbHelper, contactAggregator,
1514bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
15155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(GroupMembership.CONTENT_ITEM_TYPE,
15165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForGroupMembership(context, dbHelper, contactAggregator,
1517bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
15185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Photo.CONTENT_ITEM_TYPE,
15195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoto(context, dbHelper, contactAggregator, photoStore));
15205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Note.CONTENT_ITEM_TYPE,
15215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNote(context, dbHelper, contactAggregator));
1522bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1523bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1524bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1525bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1526bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1527bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1528bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1529bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1530bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1531bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1532bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1533bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1534bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1535bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1536bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1537bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1538bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1539bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1540bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
154115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
154215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
154315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
154415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
154515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
154615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
154715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
154815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1549bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
155015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
155115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1552bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1553bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1554bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1555bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1556bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1557bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1558bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1559bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1560bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1561bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1562bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1563bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
156415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
156515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
156615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
156715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
156815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
156915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
15705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                // Update the accounts for both the contacts and profile DBs.
157115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
15725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToContactMode();
1573bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
15745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToProfileMode();
15755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                accountsChanged |= updateAccountsInBackground(accounts);
15765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1577bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1578bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1579bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1580bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1581bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1582bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1583bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1584bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1585bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1586bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1587fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1588fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1589fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1590fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1591fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1592bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1593bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1594bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1595bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1596bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1597bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1598bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
159905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
160005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
160105e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
160205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
160305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1604bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1605bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1606bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1607bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1608bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1609bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1610bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1611bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1612bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1613bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1614bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1615f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1616f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case BACKGROUND_TASK_CLEANUP_PHOTOS: {
1617f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check rate limit.
1618f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long now = System.currentTimeMillis();
1619f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (now - mLastPhotoCleanup > PHOTO_CLEANUP_RATE_LIMIT) {
1620f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    mLastPhotoCleanup = now;
16215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
16225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // Clean up photo stores for both contacts and profiles.
16235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToContactMode();
16245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    cleanupPhotoStore();
16255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToProfileMode();
1626f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    cleanupPhotoStore();
1627f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    break;
1628f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
1629f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1630bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
16314cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
16324cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
163353fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
16343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
16353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
16364f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
16374f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
16384f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1639fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
16404cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
164151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
164251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
164351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
164451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
164551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
164651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
164751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
164851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1649bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1650f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1651f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1652f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1653f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1654f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1655f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
165651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
165751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
165851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
165951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
166051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
166151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
166251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
166351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
166451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
16655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, currentLocale);
16665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.setLocale(this, currentLocale);
1667bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1668bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1669bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
167051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1671fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1672fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1673fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1674fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1675fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1676fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1677fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1678fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
16795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mContactsHelper.getWritableDatabase();
16805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase profileDb = mProfileHelper.getWritableDatabase();
1681fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
16825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileDb.beginTransaction();
1683fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1684fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1685fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
16865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.setTransactionSuccessful();
1687fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1688fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
16895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.endTransaction();
1690fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1691fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1692fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1693fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1694fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
169505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
169605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
169705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
169805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1699bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1700bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
170151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
170251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
17033826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
17043826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
17053826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
17063826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
17073826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
17083826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
17093e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        // No accounts/no contacts status is true if there are no account and
17105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // there are no contacts or one profile contact
17113e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        if (mContactsAccountCount == 0) {
17125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long contactsNum = DatabaseUtils.queryNumEntries(mContactsHelper.getReadableDatabase(),
17133e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    Tables.CONTACTS, null);
17145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long profileNum = DatabaseUtils.queryNumEntries(mProfileHelper.getReadableDatabase(),
17155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Tables.CONTACTS, null);
17165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
17175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // TODO: Different status if there is a profile but no contacts?
17185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (contactsNum == 0 && profileNum <= 1) {
17193e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
17203e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            } else {
17213e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NORMAL);
17223e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            }
17233826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
17243826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
17253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
17263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
17273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
172831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1729f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    protected void cleanupPhotoStore() {
17305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
1731d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        mActiveDb.set(db);
17326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
17336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Assemble the set of photo store file IDs that are in use, and send those to the photo
1734f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // store.  Any photos that aren't in that set will be deleted, and any photos that no
1735f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // longer exist in the photo store will be returned for us to clear out in the DB.
17367cf50494501938f175d288077145acf49da8f171Daniel Lehmann        long photoMimeTypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
17376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Cursor c = db.query(Views.DATA, new String[]{Data._ID, Photo.PHOTO_FILE_ID},
17387cf50494501938f175d288077145acf49da8f171Daniel Lehmann                DataColumns.MIMETYPE_ID + "=" + photoMimeTypeId + " AND "
1739f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + Photo.PHOTO_FILE_ID + " IS NOT NULL", null, null, null, null);
17406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> usedPhotoFileIds = Sets.newHashSet();
17416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToDataId = Maps.newHashMap();
1742f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
1743f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            while (c.moveToNext()) {
17446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long dataId = c.getLong(0);
17456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(1);
17466802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToDataId.put(photoFileId, dataId);
17486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
17496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } finally {
17506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            c.close();
17516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
17526802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
17536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Also query for all social stream item photos.
1754c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        c = db.query(Tables.STREAM_ITEM_PHOTOS + " JOIN " + Tables.STREAM_ITEMS
1755c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " ON " + StreamItemPhotos.STREAM_ITEM_ID + "=" + StreamItemsColumns.CONCRETE_ID
1756c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " JOIN " + Tables.RAW_CONTACTS
1757c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " ON " + StreamItems.RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID,
17586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                new String[]{
1759c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_ID,
1760c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID,
1761c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotos.PHOTO_FILE_ID,
1762c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        RawContacts.ACCOUNT_TYPE,
1763c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        RawContacts.ACCOUNT_NAME
17646802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                },
17656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                null, null, null, null, null);
17666802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
17676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
1768c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        Map<Long, Account> streamItemPhotoIdToAccount = Maps.newHashMap();
17696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
17706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            while (c.moveToNext()) {
17716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemPhotoId = c.getLong(0);
17726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemId = c.getLong(1);
17736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(2);
1774c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                String accountType = c.getString(3);
1775c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                String accountName = c.getString(4);
17766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
17786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
1779c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                Account account = new Account(accountName, accountType);
1780c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                streamItemPhotoIdToAccount.put(photoFileId, account);
1781f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1782f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } finally {
1783f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            c.close();
1784f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1785f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1786f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Run the photo store cleanup.
17875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> missingPhotoIds = mPhotoStore.get().cleanup(usedPhotoFileIds);
1788f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1789d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        // If any of the keys we're using no longer exist, clean them up.  We need to do these
1790d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        // using internal APIs or direct DB access to avoid permission errors.
17916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!missingPhotoIds.isEmpty()) {
1792f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
1793d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.beginTransactionWithListener(this);
1794d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                for (long missingPhotoId : missingPhotoIds) {
1795d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    if (photoFileIdToDataId.containsKey(missingPhotoId)) {
1796d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        long dataId = photoFileIdToDataId.get(missingPhotoId);
1797d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        ContentValues updateValues = new ContentValues();
1798d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        updateValues.putNull(Photo.PHOTO_FILE_ID);
1799d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        updateData(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
1800d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                updateValues, null, null, false);
1801d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    }
1802d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
1803d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        // For missing photos that were in stream item photos, just delete the
1804d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        // stream item photo.
1805d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        long streamItemPhotoId = photoFileIdToStreamItemPhotoId.get(missingPhotoId);
1806d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        db.delete(Tables.STREAM_ITEM_PHOTOS, StreamItemPhotos._ID + "=?",
1807d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                new String[]{String.valueOf(streamItemPhotoId)});
1808d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    }
1809d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                }
1810d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.setTransactionSuccessful();
1811d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } catch (Exception e) {
1812d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                // Cleanup failure is not a fatal problem.  We'll try again later.
1813d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                Log.e(TAG, "Failed to clean up outdated photo references", e);
1814d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } finally {
1815d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.endTransaction();
1816f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1817f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1818f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1819f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
18206efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    @Override
1821b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1822b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
182331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
182431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
18256efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    @Override
18266efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected ThreadLocal<ContactsTransaction> getTransactionHolder() {
18276efb7db26598b105342d02207e0ca1c8725c10daDave Santoro        return mTransactionHolder;
18286efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    }
18296efb7db26598b105342d02207e0ca1c8725c10daDave Santoro
18305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public ProfileProvider getProfileProvider() {
18315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return new ProfileProvider(this);
18325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1834524913c66ce75ca8dec127ac88e3bc2249c246d9Dave Santoro    @VisibleForTesting
1835f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* package */ PhotoStore getPhotoStore() {
18365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mContactsPhotoStore;
1837f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1838f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1839d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    @VisibleForTesting
1840d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    /* package */ PhotoStore getProfilePhotoStore() {
1841d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        return mProfilePhotoStore;
1842d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    }
1843d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro
184487614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxDisplayPhotoDim() {
184587614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxDisplayPhotoDim;
184687614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
184787614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
184887614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxThumbnailPhotoDim() {
184987614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxThumbnailPhotoDim;
185087614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
185187614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1852013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1853013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1854013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1855013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
18565df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
18575df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
18585df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
18595df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
18605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1861ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
186272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
186372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
186472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
186572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
18665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
18675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
18685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
18695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
18705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean inProfileMode() {
18715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Boolean profileMode = mInProfileMode.get();
18725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return profileMode != null && profileMode;
18735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
18753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
18765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(
18775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1878b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
18793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18803d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1881568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1882568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1883568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1884568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1885568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1886bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1887568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1888bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1889bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1890bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1891568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1892bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, mCurrentLocale);
1894bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1895568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1896bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1897bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1898bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1899bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1900bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1901bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1902568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1903568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1904bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1905bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1906bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1907bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1908bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1909bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1910bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1911bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1912b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
19135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1914b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1915bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1916bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1917bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1918bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1919bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1920bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1921bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1922bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1923bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1924bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1925bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1926bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1927bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1928bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1929bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1930bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1931bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1932bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1933bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1934bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1935bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1936bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1937bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1938bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1939bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1940bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1941bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1942bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1943bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
19443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
19453d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
19463d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1947568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
19480e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
19493d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
19503d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1951bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1952bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1953bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1954bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1955bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1956bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
19573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
19583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
19593d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1960bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1961bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
19623d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
19633d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1964a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1965a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1966a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1967a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
19685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.wipeData();
19695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.wipeData();
19705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore.clear();
19715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore.clear();
19723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1973a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1974a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1975568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
197615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1977568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1978568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1979568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1980568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1981568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
198215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
198315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
198415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
198515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
198615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
198715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
198815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
198915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
199015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
199115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
199215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
1993ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1994568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1995568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1996568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
19975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
19985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI should be directed to the profile
19995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database rather than the contacts database.  This is true under either
20005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * of three conditions:
20015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 1. The URI itself is specifically for the profile.
20025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 2. The URI contains ID references that are in the profile ID-space.
20035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 3. The URI contains lookup key references that match the special profile lookup key.
20045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
20055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB operation to the profile database.
20065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDb(Uri uri) {
20085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return sUriMatcher.mapsToProfile(uri);
20095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI with the given values being inserted
20135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * should be directed to the profile database rather than the contacts
20145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database.  This is true if the URI already maps to the profile DB from
20155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a call to {@link #mapsToProfileDb} or if the URI matches a URI that
20165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * specifies parent IDs via the ContentValues, and the given ContentValues
20175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * contains an ID in the profile ID-space.
20185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
20195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param values The values being inserted.
20205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB insert to the profile database.
20215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDbWithInsertedValues(Uri uri, ContentValues values) {
20235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
20245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return true;
20255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int match = sUriMatcher.match(uri);
20275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (INSERT_URI_ID_VALUE_MAP.containsKey(match)) {
20285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String idField = INSERT_URI_ID_VALUE_MAP.get(match);
20295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (values.containsKey(idField)) {
20305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = values.getAsLong(idField);
20315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
20325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return true;
20335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
20345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
20355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return false;
20375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
20415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a profile operation.
20425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20436efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected void switchToProfileMode() {
20445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mProfileHelper);
20455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mProfileTransactionContext);
20465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mProfileAggregator);
20475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mProfilePhotoStore);
20485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(true);
20495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
20535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a contacts operation.
20545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20556efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected void switchToContactMode() {
20565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
20575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mContactTransactionContext);
20585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mContactAggregator);
20595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mContactsPhotoStore);
20605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(false);
20615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
206236aa19fbab3722288b7f0917166ef6990ab7b52cDave Santoro        // Clear out the active database; modification operations will set this to the contacts DB.
206336aa19fbab3722288b7f0917166ef6990ab7b52cDave Santoro        mActiveDb.set(null);
20645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2066568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2067568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
206815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
206936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
207036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
207136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
207236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
20735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDbWithInsertedValues(uri, values)) {
2074078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
2075078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.insert(uri, values);
20765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
20775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
20785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.insert(uri, values);
20795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2080568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2081568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2082568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2083568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
208415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
2085bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2086bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2087bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2088bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2089bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
2090bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2091bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2092bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
2093bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2094bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2095bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2096bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2097bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2098bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
209915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
210036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
210136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
210236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
210336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
21045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
2105078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
2106078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.update(uri, values, selection, selectionArgs);
21075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
21095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.update(uri, values, selection, selectionArgs);
21105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2111568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2112568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2113568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2114568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
211515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
211636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
211736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
211836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
211936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
21205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
2121078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
2122078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.delete(uri, selection, selectionArgs);
21235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
21255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.delete(uri, selection, selectionArgs);
21265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
21275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
21285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
21295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
21305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Replaces the current (thread-local) database to use for the operation with the given one.
21315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param db The database to use.
21325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
21335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /* package */ void substituteDb(SQLiteDatabase db) {
21345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
2135568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2136568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2137568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
213882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    public Bundle call(String method, String arg, Bundle extras) {
213982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        waitForAccess(mReadAccessLatch);
214082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (method.equals(Authorization.AUTHORIZATION_METHOD)) {
214182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Uri uri = (Uri) extras.getParcelable(Authorization.KEY_URI_TO_AUTHORIZE);
214282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
214382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Check permissions on the caller.  The URI can only be pre-authorized if the caller
214482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // already has the necessary permissions.
214582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            enforceSocialStreamReadPermission(uri);
214682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            if (mapsToProfileDb(uri)) {
214782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                mProfileProvider.enforceReadPermission(uri);
214882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
214982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
215082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // If there hasn't been a security violation yet, we're clear to pre-authorize the URI.
215182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Uri authUri = preAuthorizeUri(uri);
215282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Bundle response = new Bundle();
215382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            response.putParcelable(Authorization.KEY_AUTHORIZED_URI, authUri);
215482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            return response;
215582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        }
215682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return null;
215782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
215882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
215982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
216082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Pre-authorizes the given URI, adding an expiring permission token to it and placing that
216182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * in our map of pre-authorized URIs.
216282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @param uri The URI to pre-authorize.
216382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @return A pre-authorized URI that will not require special permissions to use.
216482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
216582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private Uri preAuthorizeUri(Uri uri) {
216682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        String token = String.valueOf(mRandom.nextLong());
216782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        Uri authUri = uri.buildUpon()
216882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                .appendQueryParameter(PREAUTHORIZED_URI_TOKEN, token)
216982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                .build();
217082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        long expiration = SystemClock.elapsedRealtime() + mPreAuthorizedUriDuration;
217182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        mPreAuthorizedUris.put(authUri, expiration);
217282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
217382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return authUri;
217482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
217582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
217682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
217782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Checks whether the given URI has an unexpired permission token that would grant access to
217882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * query the content.  If it does, the regular permission check should be skipped.
217982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @param uri The URI being accessed.
218082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @return Whether the URI is a pre-authorized URI that is still valid.
218182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
218282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    public boolean isValidPreAuthorizedUri(Uri uri) {
218382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        // Only proceed if the URI has a permission token parameter.
218482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (uri.getQueryParameter(PREAUTHORIZED_URI_TOKEN) != null) {
218582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // First expire any pre-authorization URIs that are no longer valid.
218682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            long now = SystemClock.elapsedRealtime();
218782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Set<Uri> expiredUris = Sets.newHashSet();
218882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            for (Uri preAuthUri : mPreAuthorizedUris.keySet()) {
218982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                if (mPreAuthorizedUris.get(preAuthUri) < now) {
219082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                    expiredUris.add(preAuthUri);
219182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                }
219282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
219382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            for (Uri expiredUri : expiredUris) {
219482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                mPreAuthorizedUris.remove(expiredUri);
219582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
219682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
219782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Now check to see if the pre-authorized URI map contains the URI.
219882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            if (mPreAuthorizedUris.containsKey(uri)) {
219982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                // Unexpired token - skip the permission check.
220082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                return true;
220182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
220282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        }
220382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return false;
220482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
220582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
220682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    @Override
2207078f588cef389358adabc579de00747878f3c108Dave Santoro    protected boolean yield(ContactsTransaction transaction) {
2208078f588cef389358adabc579de00747878f3c108Dave Santoro        // If there's a profile transaction in progress, and we're yielding, we need to
2209078f588cef389358adabc579de00747878f3c108Dave Santoro        // end it.  Unlike the Contacts DB yield (which re-starts a transaction at its
2210078f588cef389358adabc579de00747878f3c108Dave Santoro        // conclusion), we can just go back into a state in which we have no active
2211078f588cef389358adabc579de00747878f3c108Dave Santoro        // profile transaction, and let it be re-created as needed.  We can't hold onto
2212078f588cef389358adabc579de00747878f3c108Dave Santoro        // the transaction without risking a deadlock.
2213078f588cef389358adabc579de00747878f3c108Dave Santoro        SQLiteDatabase profileDb = transaction.removeDbForTag(PROFILE_DB_TAG);
2214078f588cef389358adabc579de00747878f3c108Dave Santoro        if (profileDb != null) {
2215078f588cef389358adabc579de00747878f3c108Dave Santoro            profileDb.setTransactionSuccessful();
2216078f588cef389358adabc579de00747878f3c108Dave Santoro            profileDb.endTransaction();
2217078f588cef389358adabc579de00747878f3c108Dave Santoro        }
2218078f588cef389358adabc579de00747878f3c108Dave Santoro
2219078f588cef389358adabc579de00747878f3c108Dave Santoro        // Now proceed with the Contacts DB yield.
2220078f588cef389358adabc579de00747878f3c108Dave Santoro        SQLiteDatabase contactsDb = transaction.getDbForTag(CONTACTS_DB_TAG);
2221078f588cef389358adabc579de00747878f3c108Dave Santoro        return contactsDb != null && contactsDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY);
2222078f588cef389358adabc579de00747878f3c108Dave Santoro    }
2223078f588cef389358adabc579de00747878f3c108Dave Santoro
2224078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
2225568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2226568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
222715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
2228078f588cef389358adabc579de00747878f3c108Dave Santoro        return super.applyBatch(operations);
2229568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2230568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
22314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
22327b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
22337b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
2234078f588cef389358adabc579de00747878f3c108Dave Santoro        return super.bulkInsert(uri, values);
22357b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
22367b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
22377b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
2238078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onBegin() {
2239bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2240b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2241b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
22425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
22435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileAggregator.clearPendingAggregations();
22445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileTransactionContext.clear();
22455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
22465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.clearPendingAggregations();
22475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactTransactionContext.clear();
22485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2249b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2250b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2251285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2252078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onCommit() {
2253bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2254b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2255b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2256b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
22575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateInTransaction(mTransactionContext.get(), mActiveDb.get());
22581a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
22591a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
22605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateAllVisible();
22611a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
22623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2263bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
2264bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
22653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
22663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
22673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
22683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
2269b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2270b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2271078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
2272078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onRollback() {
2273078f588cef389358adabc579de00747878f3c108Dave Santoro        // Not used.
2274078f588cef389358adabc579de00747878f3c108Dave Santoro    }
2275078f588cef389358adabc579de00747878f3c108Dave Santoro
2276bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
22775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleContacts = mTransactionContext.get().getStaleSearchIndexContactIds();
22785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleRawContacts = mTransactionContext.get().getStaleSearchIndexRawContactIds();
2279bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
2280bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
22815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().clearSearchIndexUpdates();
2282bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
2283bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
2284bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
2285b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2286bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2287b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2288b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
22891129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
22905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (long rawContactId : mTransactionContext.get().getInsertedRawContactIds()) {
22915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateRawContactDisplayName(mActiveDb.get(), rawContactId);
22925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().onRawContactInsert(mTransactionContext.get(), mActiveDb.get(),
22935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    rawContactId);
229424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
229524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
22965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> dirtyRawContacts = mTransactionContext.get().getDirtyRawContactIds();
2297d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
2298a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2299a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2300d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
2301a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
23025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2303a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2304a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
23055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> updatedRawContacts = mTransactionContext.get().getUpdatedRawContactIds();
2306d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
2307a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2308a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2309d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
2310a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
23115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2312b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2313b5a4add17815167d20a90645779df34cdf45280dFred Quintana
23145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Update sync states.
23155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (Map.Entry<Long, Object> entry : mTransactionContext.get().getUpdatedSyncStates()) {
2316b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
23175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (mDbHelper.get().getSyncState().update(mActiveDb.get(), id, entry.getValue()) <= 0) {
23189d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
23199d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
23209d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2321b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2322b5a4add17815167d20a90645779df34cdf45280dFred Quintana
23235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().clear();
2324b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2325b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2326a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2327a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2328a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2329a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2330d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
2331b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2332a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2333b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2334a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2335a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2336285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2337285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2338285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2339cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
234081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
234181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
234281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
234381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
234481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
234581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
234681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2347cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2348568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
234951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
23503826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
23513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
23523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
23533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
235451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
235551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2356f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
23575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
23585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return getDataRowHandlerForProfile(mimeType);
23595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
23613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
23626d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
23635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mContactsHelper, mContactAggregator, mimeType);
23643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
23653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
23663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
23673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
23683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
23695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public DataRowHandler getDataRowHandlerForProfile(final String mimeType) {
23705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        DataRowHandler handler = mProfileDataRowHandlers.get(mimeType);
23715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (handler == null) {
23725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            handler = new DataRowHandlerForCustomMimetype(
23735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mProfileHelper, mProfileAggregator, mimeType);
23745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileDataRowHandlers.put(mimeType, handler);
23755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return handler;
23775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
23785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
23794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2380de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2381bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
23821129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2383b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2384f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
23855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
23865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
2387078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
23885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2390f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2391f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2392f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2393a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2394a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
239535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2396a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
239735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
23985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
23995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mDbHelper.get().getSyncState().insert(mActiveDb.get(), values);
240035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
240135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2402d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2403d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
24046bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
24056bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
24066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
240724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
240824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
240924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
241024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
241124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2412d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
2413d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
24145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter);
2415f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2416a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2417a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2418a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2419d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
2420d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
2421d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
2422d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(segment));
2423f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2424f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2425a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2426a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2427a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
24293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
24303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
24313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24350c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
24360c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
2437f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2438f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2439a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2440a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2441a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2442ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2443f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2444f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2445ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2446ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2447ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2448eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
24495aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
245043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2451eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2452eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2453eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
24545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
24555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
245682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
24571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
24581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
24591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
24603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
24613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
24623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
24673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
24683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
24733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
24743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
24753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2479a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
248081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2481f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2482a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2483a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
24857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
24867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2488de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2489a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2490a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2491a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2492e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2493e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2494e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2495e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2496e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2497e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2498e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2499e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2500e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2501e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2502e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2503e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2504e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
25057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2506e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2507f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2508f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2509e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2510f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2511f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2512f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2513e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2514e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2515e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2516e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2517e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
25185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2519fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2520e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2521e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2522e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2523e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2524e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2525e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2526e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2527e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2528e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2529e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2530e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2531e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
25325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2533fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2534e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2535e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2536e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2537f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2538f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2539e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2540f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2541f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2542e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2543e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2544f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2545f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2546e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2547f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2548f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2549f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2550f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2551035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2552f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2553e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
25547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
25557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
255743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Resolves the account and builds an {@link AccountWithDataSet} based on the data set specified
255843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * in the URI or values (if any).
255943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param uri Current {@link Uri} being operated on.
256043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param values {@link ContentValues} to read and possibly update.
256143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     */
256243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private AccountWithDataSet resolveAccountWithDataSet(Uri uri, ContentValues values) {
25633593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final Account account = resolveAccount(uri, values);
256443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = null;
256543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (account != null) {
256643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
256743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (dataSet == null) {
25683593682b8d9213fde576a0cff54458ad50563980Dave Santoro                dataSet = values.getAsString(RawContacts.DATA_SET);
2569a71dc460ca951c7aca591f3f470c160cde70a1e3Dave Santoro            } else {
25703593682b8d9213fde576a0cff54458ad50563980Dave Santoro                values.put(RawContacts.DATA_SET, dataSet);
257143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
257243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            accountWithDataSet = new AccountWithDataSet(account.name, account.type, dataSet);
257343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        }
257443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountWithDataSet;
257543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    }
257643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
257743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    /**
2578d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
25796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
25806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
25816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
25826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2583d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2584de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
25856bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
25866bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
25876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
258824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2589a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2590f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2591f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2592dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
2593a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2594a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
25955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2596f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2597f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2598f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2599f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
260043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
26017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26023d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
26033d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2604f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
26053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
26063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
26075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long rawContactId = mActiveDb.get().insert(Tables.RAW_CONTACTS,
26085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                RawContacts.CONTACT_ID, mValues);
2609f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
26105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2611f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2612f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
26135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markNewForAggregation(rawContactId, aggregationMode);
2614285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
26155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Trigger creation of a Contact based on this RawContact at the end of transaction
26165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactInserted(rawContactId, accountWithDataSet);
2617f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2618dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2619dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2620dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2621dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2622dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2623dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2624dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2625dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
26263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2627023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2628a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2629a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2630dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2631dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2632dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2633dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2634dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2635dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2636dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2637dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2638dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
26395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.GROUPS + "," + Tables.RAW_CONTACTS,
26405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROJECTION_GROUP_ID, selection,
2641dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2642dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2643dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2644dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2645dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2646dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2647dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2648dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2649dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2650dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2651dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2652dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2653dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2654dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2655dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2656dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2657dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2658dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2659dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2660dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2661dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2662dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2663dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2664dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2665dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2666dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2667dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2668dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2669dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
26705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
26715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().insert(Tables.DATA, null, groupMembershipValues);
2672dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2673dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2674dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2675dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
26765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Long.toString(mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2677dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2678dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
26795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2680dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2681dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2682a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2683a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2684a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2685a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2686a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2687a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2688f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2689a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2690de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2691de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
269267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2693de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
269420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2695de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2696de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2697de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
26985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
2699de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2700de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2701508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2702de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2703de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2704de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2705de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2706de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
27074097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
27085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.get().getMimeTypeId(mimeType));
2709de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2710a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2711a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
27125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = rowHandler.insert(mActiveDb.get(), mTransactionContext.get(), rawContactId, mValues);
2713f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
27145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().markRawContactDirty(rawContactId);
2715a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
27165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactUpdated(rawContactId);
2717a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
27184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
27194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
27203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
27223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
27233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
27243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
27253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
27263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
27273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
27283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
27293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
27303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
27323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
27333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
27343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
27353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
27373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Ensure that the raw contact exists and belongs to the caller's account.
27393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, mValues);
27403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccount(account, rawContactId);
27413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to insert accounts params - they don't exist in the stream items table.
27436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_NAME);
27446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_TYPE);
27456802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
27463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
27475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = mActiveDb.get().insert(Tables.STREAM_ITEMS, null, mValues);
27486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (id == -1) {
27496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Insertion failed.
27506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return 0;
27516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
27523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
27543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
27553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
27563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
27573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
27593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
27633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
27643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
27653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
27663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
27676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return the stream item photo _ID of the newly created row, or 0 if there was an issue
27686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     with processing the photo or creating the row
27693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
27713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
27723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
27733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
27743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
27763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
27773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
27783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Ensure that the raw contact exists and belongs to the caller's account.
27803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Account account = resolveAccount(uri, mValues);
27813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceModifyingAccount(account, rawContactId);
27823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Don't attempt to insert accounts params - they don't exist in the stream item
27846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // photos table.
27856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_NAME);
27866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_TYPE);
27873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Process the photo and store it.
27896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (processStreamItemPhoto(mValues, false)) {
27906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Insert the stream item photo.
27915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mActiveDb.get().insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
27926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
27933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
27943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
27953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Processes the photo contained in the {@link ContactsContract.StreamItemPhotos#PHOTO}
27996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * field of the given values, attempting to store it in the photo store.  If successful,
28006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * the resulting photo file ID will be added to the values for insert/update in the table.
28016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * <p>
28026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * If updating, it is valid for the picture to be empty or unspecified (the function will
28036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * still return true).  If inserting, a valid picture must be specified.
28046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param values The content values provided by the caller.
28056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param forUpdate Whether this photo is being processed for update (vs. insert).
28066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return Whether the insert or update should proceed.
28076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
28086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    private boolean processStreamItemPhoto(ContentValues values, boolean forUpdate) {
28096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!values.containsKey(StreamItemPhotos.PHOTO)) {
28106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
28116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PHOTO);
28136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (photoBytes == null) {
28146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
28156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
28176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo and store it.
28186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
28195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long photoFileId = mPhotoStore.get().insert(new PhotoProcessor(photoBytes,
28201dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                    mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim, true), true);
28216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (photoFileId != 0) {
28226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.put(StreamItemPhotos.PHOTO_FILE_ID, photoFileId);
28236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.remove(StreamItemPhotos.PHOTO);
28246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return true;
28256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            } else {
28266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Couldn't store the photo, return 0.
28276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Log.e(TAG, "Could not process stream item photo for insert");
28286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return false;
28296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
28306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } catch (IOException ioe) {
28316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            Log.e(TAG, "Could not process stream item photo for insert", ioe);
28326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return false;
28336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
28356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
28366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
28373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
28383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
28393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
28403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
28423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
28435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
28445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItems.RAW_CONTACT_ID},
28453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
28463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
28473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
28483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
28493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
28503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
28523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
28533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
28553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
28563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
285836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * If the given URI is reading stream items or stream photos, this will run a permission check
285936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * for the android.permission.READ_SOCIAL_STREAM permission - otherwise it will do nothing.
286036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * @param uri The URI to check.
286136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     */
286236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private void enforceSocialStreamReadPermission(Uri uri) {
286382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (SOCIAL_STREAM_URIS.contains(sUriMatcher.match(uri))
286482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                && !isValidPreAuthorizedUri(uri)) {
286536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            getContext().enforceCallingOrSelfPermission(
286636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    "android.permission.READ_SOCIAL_STREAM", null);
286736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        }
286836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    }
286936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
287036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    /**
287136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * If the given URI is modifying stream items or stream photos, this will run a permission check
287236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * for the android.permission.WRITE_SOCIAL_STREAM permission - otherwise it will do nothing.
287336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * @param uri The URI to check.
287436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     */
287536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private void enforceSocialStreamWritePermission(Uri uri) {
287636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        if (SOCIAL_STREAM_URIS.contains(sUriMatcher.match(uri))) {
287736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            getContext().enforceCallingOrSelfPermission(
287836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    "android.permission.WRITE_SOCIAL_STREAM", null);
287936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        }
288036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    }
288136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
288236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    /**
28833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given raw contact ID is owned by the given account.
28843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account is null, this will return true iff the raw contact
28853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * is also associated with the "null" account.
28863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
28873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account does not match, this will throw a security exception.
28883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
28893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to check for.
28903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void enforceModifyingAccount(Account account, long rawContactId) {
28923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
28933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
28943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
28953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
28963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
28973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
28983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c;
28993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (account != null) {
29005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c = mActiveDb.get().query(Tables.RAW_CONTACTS,
29015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{RawContactsColumns.CONCRETE_ID}, accountSelection,
29023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
29033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
29043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } else {
29055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c = mActiveDb.get().query(Tables.RAW_CONTACTS,
29065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{RawContactsColumns.CONCRETE_ID}, noAccountSelection,
29075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{String.valueOf(rawContactId)},
29083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
29093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if(c.getCount() == 0) {
29123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new SecurityException("Caller account does not match raw contact ID "
29133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    + rawContactId);
29143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream items matches up with the given
29223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
29233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
29243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
29253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
29263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
29273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item IDs that would be included in this selection.
29283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
29303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
29313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = Lists.newArrayList();
29323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItems(qb);
29345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = qb.query(mActiveDb.get(),
29353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
29363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
29373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
29393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemIds.add(c.getLong(0));
29403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
29423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
29433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds;
29483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream item photos matches up with the given
29523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
29533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
29543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
29553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
29563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
29573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item photo IDs that would be included in this selection.
29583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
29603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
29613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemPhotoIds = Lists.newArrayList();
29623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItemPhotos(qb);
29645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = qb.query(mActiveDb.get(),
29655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
29663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
29673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
29693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemPhotoIds.add(c.getLong(0));
29703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
29723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
29733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemPhotoIds;
29783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
29823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
29833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
29843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
29853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
29863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
29873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
29883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
29893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
29903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
29923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
29935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
29943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
29953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
29963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
29983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
29993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
30003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
30013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
30023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
30033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
30043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
30053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
30063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
30073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
30083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
30093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
30103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
30113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
30123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
30133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
30143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
30153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
30163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
30173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
30183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
30199261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
302020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
302120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
3022f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
302320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
302420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3025de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3026de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
30270c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Uri dataUri = inProfileMode()
30280c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                ? Uri.withAppendedPath(Profile.CONTENT_URI, RawContacts.Data.CONTENT_DIRECTORY)
30290c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                : Data.CONTENT_URI;
30300c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Cursor c = query(dataUri, DataRowHandler.DataDeleteQuery.COLUMNS,
3031f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
3032de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
3033de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
3034f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
3035f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
3036a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
30375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count += rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
3038f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
30395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mTransactionContext.get().markRawContactDirty(rawContactId);
304088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
304120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
304220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
3043de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
304420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
304520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
304620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
304720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
304820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
304988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
305088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
305188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
305220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
3053f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
305488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
305588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
30564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
3057f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
30584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
3059f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
306020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
306120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
306220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
306320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
306420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3065f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
306620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
306720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
306820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
306920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
307020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
307120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
307220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
307320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
307420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
30757a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
307620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
307720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3078a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
30795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
308020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
308120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
308220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
308320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
308420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
308520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
3086ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
3087ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
3088f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
3089f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
3090f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
3091f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
30923593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
3093ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3094ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
3095f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
309667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
30975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
309867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
3099f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
3100ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3101dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
3102dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
3103dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
3104dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3105f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
3106f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
310773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
310873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
31095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long result = mActiveDb.get().insert(Tables.GROUPS, Groups.TITLE, mValues);
3110ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3111dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
3112dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
3113dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
3114dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
31153593682b8d9213fde576a0cff54458ad50563980Dave Santoro            if (accountWithDataSet == null) {
3116dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
311743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + " IS NULL AND "
311843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + " IS NULL";
3119dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
31203593682b8d9213fde576a0cff54458ad50563980Dave Santoro            } else if (accountWithDataSet.getDataSet() == null) {
3121dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
31223593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
31233593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.DATA_SET + " IS NULL";
31243593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
31253593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
31263593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType()
31273593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
312843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            } else {
312943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selection = RawContacts.ACCOUNT_NAME + "=? AND "
313043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
313143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + "=?";
31323593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
31333593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
31343593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType(),
31353593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getDataSet()
31363593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
3137dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
31385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3139dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
3140dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
3141892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
3142892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
3143892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
3144892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
3145892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
31465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mTransactionContext.get().markRawContactDirty(rawContactId);
3147892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
3148dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3149892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
3150892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
3151dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3152dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3153dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3154f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
31551a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3156ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
3157ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3158ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
3159ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3160ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31615aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
3162f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // Before inserting, ensure that no settings record already exists for the
3163f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // values being inserted (this used to be enforced by a primary key, but that no
3164f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // longer works with the nullable data_set field added).
3165f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountName = values.getAsString(Settings.ACCOUNT_NAME);
3166f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountType = values.getAsString(Settings.ACCOUNT_TYPE);
3167f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String dataSet = values.getAsString(Settings.DATA_SET);
3168f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Uri.Builder settingsUri = Settings.CONTENT_URI.buildUpon();
3169f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountName != null) {
3170f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_NAME, accountName);
3171f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3172f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountType != null) {
3173f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_TYPE, accountType);
3174f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3175f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (dataSet != null) {
3176f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.DATA_SET, dataSet);
3177f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3178f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Cursor c = queryLocal(settingsUri.build(), null, null, null, null, 0);
3179f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        try {
3180f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (c.getCount() > 0) {
31810e21a867a572679d64d79041eb574d13665178d4Dave Santoro                // If a record was found, replace it with the new values.
31820e21a867a572679d64d79041eb574d13665178d4Dave Santoro                String selection = null;
31830e21a867a572679d64d79041eb574d13665178d4Dave Santoro                String[] selectionArgs = null;
31840e21a867a572679d64d79041eb574d13665178d4Dave Santoro                if (accountName != null && accountType != null) {
31850e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    selection = Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=?";
31860e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    if (dataSet == null) {
31870e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selection += " AND " + Settings.DATA_SET + " IS NULL";
31880e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selectionArgs = new String[] {accountName, accountType};
31890e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    } else {
31900e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selection += " AND " + Settings.DATA_SET + "=?";
31910e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selectionArgs = new String[] {accountName, accountType, dataSet};
31920e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    }
31930e21a867a572679d64d79041eb574d13665178d4Dave Santoro                }
31940e21a867a572679d64d79041eb574d13665178d4Dave Santoro                return updateSettings(uri, values, selection, selectionArgs);
3195f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            }
3196f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        } finally {
3197f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            c.close();
3198f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3199f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro
3200f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // If we didn't find a duplicate, we're fine to insert.
32015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long id = mActiveDb.get().insert(Tables.SETTINGS, null, values);
32025aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
32031a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
32041a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3205e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
32061a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
3207e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
3208e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3209e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3210ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
321182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
32121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
321382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
321482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
32150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
32164dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
32174dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
32180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
321982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
32204dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
32214dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
32224dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
32234dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
32241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
32251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3226dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3227dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
322882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
32296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountType = null;
32306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountName = null;
3231f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
32322526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3233dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3234dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3235dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32362526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
32372526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
32381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3239dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3240dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
32420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
32430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
32440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3245dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3246dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3247dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String mimeTypeIdIm = String.valueOf(mDbHelper.get().getMimeTypeIdForIm());
3249dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
32505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String mimeTypeIdEmail = String.valueOf(mDbHelper.get().getMimeTypeIdForEmail());
3251f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3252f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3253f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3254f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3255f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3256f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3257f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
32582526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
32592526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
32602526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
32612526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
32622526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32632526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
32642526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32652526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3266dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32682526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3269dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
32702526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
32712526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3272dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
32732526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
32742526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
32752526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
32762526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
32782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3279dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32802526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32812526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3282dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3283dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
32841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
328582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
32862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
32872526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3288dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
328970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
329070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
32911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
32921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
32935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = mActiveDb.get().query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
32942526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
32954394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
32961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
329767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
32985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
32996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountType = cursor.getString(DataContactsQuery.ACCOUNT_TYPE);
33006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountName = cursor.getString(DataContactsQuery.ACCOUNT_NAME);
3301e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
33021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
33031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
33041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
33051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
33061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
330731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
330831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
330931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
33101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
33111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
331282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3313a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3314a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3315a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3316a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3317a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3318a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3319a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
332082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3321a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3322a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
332382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
332482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
332582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
332682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
332782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3328a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
332982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
333082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3331aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3332aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
33331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3334a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
33355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().replace(Tables.PRESENCE, null, mValues);
3336a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3337e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
33380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
333982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
334082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
33410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
33420bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Resources resources = getContext().getResources();
33430bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!TextUtils.isEmpty(resPackage)) {
33440bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                PackageManager pm = getContext().getPackageManager();
33450bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                try {
33460bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    resources = pm.getResourcesForApplication(resPackage);
33470bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                } catch (NameNotFoundException e) {
33480bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    Log.w(TAG, "Contact status update resource package not found: "
33490bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            + resPackage);
33500bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                }
33510bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
33520bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer labelResourceId = values.getAsInteger(StatusUpdates.STATUS_LABEL);
33530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33540bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if ((labelResourceId == null || labelResourceId == 0) && protocol != null) {
33550bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                labelResourceId = Im.getProtocolLabelResource(protocol);
33560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
33570bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String labelResource = getResourceName(resources, "string", labelResourceId);
33580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33590bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer iconResourceId = values.getAsInteger(StatusUpdates.STATUS_ICON);
33600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
33610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33620bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String iconResource = getResourceName(resources, "drawable", iconResourceId);
33630bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
3364a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
33655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().deleteStatusUpdate(dataId);
3366a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
33676802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
33686802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (timestamp != null) {
33695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().replaceStatusUpdate(dataId, timestamp, status, resPackage,
33700bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            iconResourceId, labelResourceId);
33716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                } else {
33725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().insertStatusUpdate(dataId, status, resPackage, iconResourceId,
33730bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            labelResourceId);
33746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
33756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // For forward compatibility with the new stream item API, insert this status update
33776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // there as well.  If we already have a stream item from this source, update that
33786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // one instead of inserting a new one (since the semantics of the old status update
33796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // API is to only have a single record).
33806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (rawContactId != -1 && !TextUtils.isEmpty(status)) {
33816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ContentValues streamItemValues = new ContentValues();
33826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3383d5ef5903570e533a501abe6a8e3d533fdb5318fcFlavio Lerda                    // Status updates are text only but stream items are HTML.
3384e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda                    streamItemValues.put(StreamItems.TEXT, statusUpdateToHtml(status));
33856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.COMMENTS, "");
33866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_PACKAGE, resPackage);
33876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_ICON, iconResource);
33886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_LABEL, labelResource);
33896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TIMESTAMP,
33906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            timestamp == null ? System.currentTimeMillis() : timestamp);
33916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Note: The following is basically a workaround for the fact that status
33936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates didn't do any sort of account enforcement, while social stream item
33946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates do.  We can't expect callers of the old API to start passing account
33956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // information along, so we just populate the account params appropriately for
339643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // the raw contact.  Data set is not relevant here, as we only check account
339743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // name and type.
33986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    if (accountName != null && accountType != null) {
33996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName);
34006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType);
34016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
34026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
34036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Check for an existing stream item from this source, and insert or update.
34046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Uri streamUri = StreamItems.CONTENT_URI;
340536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    Cursor c = queryLocal(streamUri, new String[]{StreamItems._ID},
34066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.RAW_CONTACT_ID + "=?",
340736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            new String[]{String.valueOf(rawContactId)},
340836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            null, -1 /* directory ID */);
34096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    try {
34106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        if (c.getCount() > 0) {
34116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            c.moveToFirst();
341236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            updateInTransaction(ContentUris.withAppendedId(streamUri, c.getLong(0)),
34136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    streamItemValues, null, null);
34146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        } else {
341536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            insertInTransaction(streamUri, streamItemValues);
34166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        }
34176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    } finally {
34186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        c.close();
34196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
34206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
3421e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3422e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3423bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3424a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
34255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().updateLastStatusUpdateId(contactId);
3426a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3427a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3428a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
34291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
34301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3431e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    /** Converts a status update to HTML. */
3432e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    private String statusUpdateToHtml(String status) {
34334747809486541f7a3d342d3e1dd48fb5ea255ad6Flavio Lerda        return TextUtils.htmlEncode(status);
3434e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    }
3435e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda
34360bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    private String getResourceName(Resources resources, String expectedType, Integer resourceId) {
34370bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        try {
34380bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (resourceId == null || resourceId == 0) return null;
34390bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34400bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            // Resource has an invalid type (e.g. a string as icon)? ignore
34410bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceEntryName = resources.getResourceEntryName(resourceId);
34420bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceTypeName = resources.getResourceTypeName(resourceId);
34430bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!expectedType.equals(resourceTypeName)) {
34440bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                Log.w(TAG, "Resource " + resourceId + " (" + resourceEntryName + ") is of type " +
34450bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                        resourceTypeName + " but " + expectedType + " is required.");
34460bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                return null;
34470bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
34480bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34490bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return resourceEntryName;
34500bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        } catch (NotFoundException e) {
34510bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return null;
34520bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        }
34530bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    }
34540bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3456de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3457bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3458b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3459b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
34605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
34615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
34625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
3463078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
34645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
34655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
3466b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3467f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3468f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3469508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3470508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
347135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
34725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
34735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selection,
34745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
34765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case SYNCSTATE_ID: {
34775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
34785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
34795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
34805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selectionWithId,
34815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
348335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
34845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
3485b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3486b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3487b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
34885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().delete(mActiveDb.get(), selectionWithId,
34895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
3491b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3492cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3493cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3494cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3495cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3496cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3497d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3498d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3499dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
35006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
35016bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
35029fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
35032e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
35042e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
35052e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
35065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3507fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
35082e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
35092e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
35105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3511dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
35122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
35132e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
35149fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
35159fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
35169fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
35179fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
35189fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
35199fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3520a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
35219fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
35229fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
35239fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
35249fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
35259fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
35269fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
35279fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
35289fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
352960de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
35309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
35315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = query(mActiveDb.get(), lookupQb, null, selection, args, null, null,
35325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
35339fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
35349fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
35359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3536dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
35379fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
35389fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
35399fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
35409fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
35419fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
35429fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
35439fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
35449fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
35459fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
35469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
3547d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
3548d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
35492971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
35505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3551fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3552e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
35532971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
35542971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
35552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3556fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3557fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3558fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
35592971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
35602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
35612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
35622971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
35632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
35642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
35652971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
3566d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
3567d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
35682971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
35695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return deleteRawContact(rawContactId, mDbHelper.get().getContactId(rawContactId),
3570fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3571508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3572508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
35730c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
35740c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
3575f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3576944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3577f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
357820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
357920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
358048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
358148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
358248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
3583d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case POSTALS_ID:
3584d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
3585508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3586f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
35874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
35884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3589ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3590ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3591ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3592f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
35935aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
35942971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
35952971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
35962971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
35972971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
35985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups._ID},
3599e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
36002971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
36012971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
36025aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
36032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
36042971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
36052971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
36062971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
360781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3608f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
360981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
36102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3611508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3612508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3613eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
361443880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3615e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3616eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3617eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
36185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
36195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
36200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
36211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
36221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
36233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
36243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
36263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
36293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
36319b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                        StreamItems._ID + "=?",
36323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
36333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
363582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
363682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
363782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
363882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
363982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                return deleteStreamItems(uri, new ContentValues(),
364082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
364182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
364282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
364382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
364482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
36453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
36463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36475d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String streamItemId = uri.getPathSegments().get(1);
36485d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String selectionWithId =
36495d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        (StreamItemPhotos.STREAM_ITEM_ID + "=" + streamItemId + " ")
36505d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                                + (selection == null ? "" : " AND (" + selection + ")");
36515d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                return deleteStreamItemPhotos(uri, new ContentValues(),
36525d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        selectionWithId, selectionArgs);
36533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
36563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
36583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
36593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
36603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
36613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
36623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
36633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
366581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
366681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
36673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
366881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3669508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
36704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
36714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36721c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3673ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
36745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long groupMembershipMimetypeId = mDbHelper.get()
367594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
36765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
367794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
367894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
367994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
368094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3681f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
36825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
368394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
368494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
368594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3686f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
36875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId,
36885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
368994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
369094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
36911a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
369294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
369394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
369494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
36955aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
36965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().delete(Tables.SETTINGS, selection, selectionArgs);
36971a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3698e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3699e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3700e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3701dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
370296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
37035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
370496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
370596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3706cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3707cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3708cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3709dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3710cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3711cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3712cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3713cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3714cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
37153826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
37163826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
37175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3718cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3719cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3720fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
37215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
37223826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
37233826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
372482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        // Find and delete stream items associated with the raw contact.
372582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
372682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                new String[]{StreamItems._ID},
372782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
372882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                null, null, null);
372982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        try {
373082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            while (c.moveToNext()) {
373182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                deleteStreamItem(c.getLong(0));
373282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
373382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        } finally {
373482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            c.close();
373582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        }
373682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
3737d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        if (callerIsSyncAdapter || rawContactIsLocal(rawContactId)) {
37385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().delete(Tables.PRESENCE,
37395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
37405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            int count = mActiveDb.get().delete(Tables.RAW_CONTACTS,
37415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    RawContacts._ID + "=" + rawContactId, null);
374241f76a59a31946f6d784dacf9f13d9a4c0bbe203Dave Santoro            mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
3743fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
374433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
37455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().removeContactIfSingleton(rawContactId);
3746dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
374733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
374833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
374933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3750d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    /**
3751d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     * Returns whether the given raw contact ID is local (i.e. has no account associated with it).
3752d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     */
3753d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    private boolean rawContactIsLocal(long rawContactId) {
3754d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3755d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {
3756d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.ACCOUNT_NAME,
3757d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.ACCOUNT_TYPE,
3758d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.DATA_SET
3759d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                },
3760d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                RawContacts._ID + "=?",
3761d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {String.valueOf(rawContactId)}, null, null, null);
3762d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        try {
3763d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            return c.moveToFirst() && c.isNull(0) && c.isNull(1) && c.isNull(2);
3764d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        } finally {
3765d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            c.close();
3766d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        }
3767d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    }
3768d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro
37690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
37709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
37719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
37729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
37739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
37749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
37755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      mActiveDb.get().delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
37769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
37775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      return mActiveDb.get().delete(Tables.PRESENCE, selection, selectionArgs);
37780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
37790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
37803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
37813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
37823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream items to be deleted, and check that they belong
37833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // to the account.
37843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
37853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
37863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                account, selection, selectionArgs);
37873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
37893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        for (long streamItemId : streamItemIds) {
37903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            deleteStreamItem(streamItemId);
37913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
37923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mVisibleTouched = true;
37943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds.size();
37953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
37963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
37983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
37993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
38005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
38013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
38023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
38053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
38063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream item photos to be deleted, and check that they
38073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // belong to the account.
38083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
38093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
38103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
38125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
38133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
38163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
38175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS,
38185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                StreamItemPhotos.STREAM_ITEM_ID + "=?",
38193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
38203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3822dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
382381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
382481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3825cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3826cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3827cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3828cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3829cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3830cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3831dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3832cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3833cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
38344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3835de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3836de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3837bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3838b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3839b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3840b5a4add17815167d20a90645779df34cdf45280dFred Quintana
38415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
38425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
3843078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
38445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
38455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
384635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
384700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
384800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3849b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3850b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
38511129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
38525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().syncStateUpdated(rowId, data);
3853b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3854b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3855b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3856f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3857f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
385800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
385935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
38605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
38615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
3862b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3863b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3864b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3865b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3866b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3867b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3868b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
38695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
38705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionWithId, selectionArgs);
38715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
38725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
38735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
38745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection = appendAccountToSelection(uri, selection);
38755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
38765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
38775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
38785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().update(mActiveDb.get(), values,
3879b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3880b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
388135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3882d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case CONTACTS:
3883d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE: {
3884dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
388500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
388600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
388700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3888d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3889dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3890c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3891c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3892c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
38932e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
38942e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
38952e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
38962e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
38972e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
38985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3899fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
39002e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
39012e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
39025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3903dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
39042e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
39052e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
39062e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
3907d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
3908d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
3909d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
3910d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                final String rawContactId = uri.getPathSegments().get(segment);
39117d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
39127d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
39137d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
39147d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
39157d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
39167d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
39177d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
39187d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
39190c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
39200c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
3921944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3922f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
392381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3924f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
392581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
392620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
392720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3928c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
392948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
393048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
393148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
393248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3933f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
393481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3935f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
393681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
393700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
393800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
39397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case RAW_CONTACTS:
39415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_RAW_CONTACTS: {
39425ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3943dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
39447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
39457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
394833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
39494529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
39504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
39514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3952dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3953dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
39544529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
39554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3956dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3957dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
39584529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
39597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
39607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3962ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
39635aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3964f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
396581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3966f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
396781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3968ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3969ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3970ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3971ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3972ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
39734da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
39744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
397573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
39765aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
39775aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
397881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3979f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
398081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3981ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3982ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3983ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3984127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
39855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count = updateAggregationException(mActiveDb.get(), values);
3986b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3987b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3988b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3989eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3990e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3991e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
399243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3993eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3994eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3995eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
39965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
39975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
39989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
39999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
40009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
40019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
40033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
40043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
40089b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                count = updateStreamItems(uri, values, StreamItems._ID + "=?",
40093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
40103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
401382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
401482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
401582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
401682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                count = updateStreamItems(uri, values,
401782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
401882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
401982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
402082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
402182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
40223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
40233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
40243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
40283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
40293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
40303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
40313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
40353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
40363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
40373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
40383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
40393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
40403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
40413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
404472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
4045bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
404672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
4047d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4048d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4049d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
405046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
405146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
405246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
405346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
405446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
405546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
405646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
405746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
405846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
405981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
406081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
4061f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
406281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
406300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
406400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
406500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
40664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
40674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
40699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
40709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
40719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
40729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
40739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
40749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
40755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.STATUS_UPDATES,
40769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
40779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
40789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
40799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
40809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
40829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
40839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
40845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.PRESENCE, settableValues,
40859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
40869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
40879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
40889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
40899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
40909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
40919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
40933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
40943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
40953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
40963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream items being updated belong to the account.
40983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
40993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
41003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream items table.
41026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
41036802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
41046802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
41053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
41065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
41073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
41083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
41103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
41113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
41123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
41133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream item photos being updated belong to the account.
41153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
41163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
41173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream item
41196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // photos table.
41206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
41216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
41226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
41236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo (since we're updating, it's valid for the photo to not be present).
41246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (processStreamItemPhoto(values, true)) {
41256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // If there's been no exception, the update should be fine.
41265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mActiveDb.get().update(Tables.STREAM_ITEM_PHOTOS, values, selection,
41275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selectionArgs);
41286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
41296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return 0;
41303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
41313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
41339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
41349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
41359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
41369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
41379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
41389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
41399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
41409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
41419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
41449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
41459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
41469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
41479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
41489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
41499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
41509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
41519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
41529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
41539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
41549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
41559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
41569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
41599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
41609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
41619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
4162aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
4163aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
41649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
41659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41675aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
4168f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
416973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4170ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
4171ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
417273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
4173f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
417473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
417573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
417673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
417773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
417873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
417973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
418073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
418173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
41825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.GROUPS, updatedValues, selectionWithId,
41835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selectionArgs);
41841a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
41851a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
418694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
418743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
418843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // TODO: This will not work for groups that have a data set specified, since the content
418943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // resolver will not be able to request a sync for the right source (unless it is updated
419043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // to key off account with data set).
41916ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
41921129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
41935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
4194e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
41956ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
41966ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
41976ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
41986ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
41996ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
42006ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
42016ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
420224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
42036ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
4204ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
42056ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
42066ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
42076ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
42086ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
42096ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
42106ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
42116ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
42126ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
421394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
421494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
421594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
4216b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
4217b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
42185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().update(Tables.SETTINGS, values, selection, selectionArgs);
42191a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
42201a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
4221e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
4222e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
4223e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
4224e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4225dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
4226dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
42274529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
42284529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
42294529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
42304529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
423173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
423297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
423397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
423497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
423597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
423697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
42374529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
42385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
423951bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
42404529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
42414529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
42424529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
42434529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
4244dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
42454529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
42464529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
42474529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
42484529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
42494529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
42504529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
42514529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
42524529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
42534529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
4254dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
4255dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
425696b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
425796b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
425819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
425919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
426019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
4261ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
4262ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
426343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet = null;
426419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
42655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
42665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selection, mSelectionArgs1, null, null, null);
426719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
426819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
426919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
4270ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
4271ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
427243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    dataSet = cursor.getString(RawContactsQuery.DATA_SET);
427319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
427419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
427519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
427619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
427719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
427819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
427919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
4280f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
42815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
42825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
4283f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
4284f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
4285f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
4286f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
4287f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
4288f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
42895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().markForAggregation(rawContactId, aggregationMode, false);
4290f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
4291f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
4292433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
4293dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
4294dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4295dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
4296dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
42975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateStarred(rawContactId);
4298dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
4299dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
4300dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
4301dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
4302dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
4303dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
43045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    boolean starred = 0 != DatabaseUtils.longForQuery(mActiveDb.get(),
4305dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
4306dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
4307dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
4308dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4309dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4310dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4311dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
4312dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
4313dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
4314dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
4315433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
4316dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4317285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
43185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateLookupKeyForRawContact(mActiveDb.get(), rawContactId);
4319285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
4320f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
4321f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
4322f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
4323f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
4324f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
43255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().resetNameVerifiedForOtherRawContacts(rawContactId);
4326f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
43275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateDisplayNameForRawContact(mActiveDb.get(), rawContactId);
4328f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
432919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
43305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mTransactionContext.get().rawContactInserted(rawContactId,
433143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new AccountWithDataSet(accountName, accountType, dataSet));
433219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
43335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
43345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
433533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
433633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
4337321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
4338f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
433920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
434020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
434120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
43425ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
434320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
434420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
434520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
434620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
434720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
43485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
434920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
435020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
435197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
435297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
435397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
435497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
435597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
4356653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
435720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4358653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
4359653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
43605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = queryLocal(uri,
4361f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                DataRowHandler.DataUpdateQuery.COLUMNS,
43625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection, selectionArgs, null, -1 /* directory ID */);
4363653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
4364653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
4365f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
436620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
4367653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
4368653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
436920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
437020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4371653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
437220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
437320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4374f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
4375653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
4376653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
4377321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
4378653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
4379f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
4380a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
4381f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean updated =
43825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                rowHandler.update(mActiveDb.get(), mTransactionContext.get(), values, c,
43835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        callerIsSyncAdapter);
4384f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
4385f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
4386a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
4387f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return updated ? 1 : 0;
4388321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
4389321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
43908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
4391dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
43928c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
43935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.CONTACTS,
43945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[] { Contacts._ID }, selection, selectionArgs, null, null, null);
43958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
43968c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
43978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
439824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4399dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
44008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
44018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
44028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
44038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
44048c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
44058c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
44068c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
44078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
44088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4409dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
4410dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
4411d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
44128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4413b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
4414d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4415b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
4416d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4417b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
4418d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4419b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
4420d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4421b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
4422d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
4423d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4424d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
44258c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
4426d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
4427d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
4428d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
44298c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
4430c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
44318c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
4432c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
4433c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
44344da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
44355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
443697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
44378c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4438dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
44395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
4440dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
4441dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
4442dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
4443dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4444dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4445dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4446dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4447dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4448dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4449dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4450dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4451dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4452dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
44538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
44548c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
44558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4456b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
44578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4458b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
44598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4460b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
44618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4462b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
44638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4464b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
44658c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
44668c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
44675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int rslt = mActiveDb.get().update(Tables.CONTACTS, mValues, Contacts._ID + "=?",
44685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mSelectionArgs1);
44696e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
44709b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
44719b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
44725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
44735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
44749b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
44759b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4476f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4477d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4478127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4479127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
44800c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
44810c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
448280c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
4483ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
4484ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
44850c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
44860c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
44870c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
44880c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
44890c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
44900c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4491b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4492127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
44930c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
44944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
44954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
44960c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
44974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
44984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
44990c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
45006bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
45016bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
45020c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
45030c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
45040c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
45050c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4506127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4507127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
45085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
45095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId1,
451069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
45115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId2,
451269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4513dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
45145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId1);
45155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId2);
4516127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4517127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4518127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4519127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4520b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4521b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
452270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4523bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
45243826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
45253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4526bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
4527f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4528e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
45295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
45305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
45315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        db.beginTransaction();
45325dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro
45335dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro        // WARNING: This method can be run in either contacts mode or profile mode.  It is
45345dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro        // absolutely imperative that no calls be made inside the following try block that can
45355dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro        // interact with the contacts DB.  Otherwise it is quite possible for a deadlock to occur.
453670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
453743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> existingAccountsWithDataSets =
453843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.ACCOUNTS);
4539743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
454043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Add a row to the ACCOUNTS table (with no data set) for each new account.
4541743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
454243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
454343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        account.name, account.type, null);
454443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!existingAccountsWithDataSets.contains(accountWithDataSet)) {
4545e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
454643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
454743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // Add an account entry with an empty data set to match the account.
45485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
454943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
455043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ") VALUES (?, ?, ?)",
455143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new String[] {
455243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
455343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
455443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
455543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            });
4556743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4557743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
455848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
455943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Check each of the existing sub-accounts against the account list.  If the owning
456043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // account no longer exists, the sub-account and all its data should be deleted.
456143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<AccountWithDataSet> accountsWithDataSetsToDelete =
456243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    new ArrayList<AccountWithDataSet>();
456343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<Account> accountList = Arrays.asList(accounts);
456443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : existingAccountsWithDataSets) {
456543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                Account owningAccount = new Account(
456643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountWithDataSet.getAccountName(), accountWithDataSet.getAccountType());
456743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!accountList.contains(owningAccount)) {
456843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSetsToDelete.add(accountWithDataSet);
456943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                }
457070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
457170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
457243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (!accountsWithDataSetsToDelete.isEmpty()) {
4573e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
457443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
457543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    Log.d(TAG, "removing data for removed account " + accountWithDataSet);
457643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountParams = new String[] {
457743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountName(),
457843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountType()
457943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    };
458043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountWithDataSetParams = accountWithDataSet.getDataSet() == null
458143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            ? accountParams
458243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            : new String[] {
458343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
458443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
458543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
458643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            };
458743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String groupsDataSetClause = " AND " + Groups.DATA_SET
458843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
458943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String rawContactsDataSetClause = " AND " + RawContacts.DATA_SET
459043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
4591f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                    String settingsDataSetClause = " AND " + Settings.DATA_SET
4592f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
459343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
45945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4595e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4596e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
459743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + Groups.ACCOUNT_TYPE + " = ?" +
459843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    groupsDataSetClause, accountWithDataSetParams);
45995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4600e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4601e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4602e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4603e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4604e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
460543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
460643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    rawContactsDataSetClause + ")", accountWithDataSetParams);
46075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4608c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            "DELETE FROM " + Tables.STREAM_ITEM_PHOTOS +
4609c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            " WHERE " + StreamItemPhotos.STREAM_ITEM_ID + " IN (" +
4610c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    "SELECT " + StreamItems._ID +
4611c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " FROM " + Tables.STREAM_ITEMS +
4612c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
4613c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            "SELECT " + RawContacts._ID +
4614c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " FROM " + Tables.RAW_CONTACTS +
4615c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4616c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
4617c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            rawContactsDataSetClause + "))",
4618c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            accountWithDataSetParams);
4619c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                    db.execSQL(
4620c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            "DELETE FROM " + Tables.STREAM_ITEMS +
4621c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
4622c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    "SELECT " + RawContacts._ID +
4623c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " FROM " + Tables.RAW_CONTACTS +
4624c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4625c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
4626c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    rawContactsDataSetClause + ")",
4627c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            accountWithDataSetParams);
4628c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                    db.execSQL(
4629e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4630e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
463143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
463243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
46335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4634e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4635e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4636f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            " AND " + Settings.ACCOUNT_TYPE + " = ?" +
4637f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            settingsDataSetClause, accountWithDataSetParams);
46385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4639e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4640e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
464143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + "=?" +
464243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
46435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4644d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4645d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
464643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + Directory.ACCOUNT_TYPE + "=?", accountParams);
46474458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4648e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4649e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
465033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
465133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4652e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
465333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
46545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = db.rawQuery("SELECT " + Contacts._ID +
465533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
465633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
465769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
465869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
465969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
466033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
466133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
466269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
466369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
466433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
466533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
466633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
466733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
466833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
466933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
467033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
467133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
467233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
46735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
467433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
46755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().updateAllVisible();
46765dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro
46775dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                // Don't bother updating the search index if we're in profile mode - there is no
46785dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                // search index for the profile DB, and updating it for the contacts DB in this case
46795dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                // makes no sense and risks a deadlock.
46805dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                if (!inProfileMode()) {
46815dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                    updateSearchIndexInTransaction();
46825dccfb059f5df0e9fdba026bcfbed677f44922cdDave Santoro                }
468333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
468433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
468543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Now that we've done the account-based additions and subtractions from the Accounts
468643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // table, check for raw contacts that have been added with a data set and add Accounts
468743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // entries for those if necessary.
468843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            existingAccountsWithDataSets = findValidAccountsWithDataSets(Tables.ACCOUNTS);
468943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> rawContactAccountsWithDataSets =
469043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.RAW_CONTACTS);
469143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            rawContactAccountsWithDataSets.removeAll(existingAccountsWithDataSets);
469243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
469343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Any remaining raw contact sub-accounts need to be added to the Accounts table.
469443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : rawContactAccountsWithDataSets) {
469543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                accountsChanged = true;
469643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
469743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // Add an account entry to match the raw contact.
46985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
469943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
470043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ") VALUES (?, ?, ?)",
470143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new String[] {
470243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountName(),
470343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountType(),
470443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getDataSet()
470543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        });
470643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
470743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
4708e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
470943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // TODO: Should sync state take data set into consideration?
47105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getSyncState().onAccountsChanged(db, accounts);
4711e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
47125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
471370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
47145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.endTransaction();
471570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
471673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
47173826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
47183826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
47193826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
47203826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
47213826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
47223826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4723afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
472470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4725619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
47263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
47273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
47283826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
47293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
47303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
47313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
47323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
47333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
47343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
47353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
47363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
47373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
47383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
47393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
47403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
47413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
47423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
47433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
47443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
47453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
474672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
4747bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
4748d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4749d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4750619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
475143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Finds all distinct account types and data sets present in the specified table.
4752627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
475343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private Set<AccountWithDataSet> findValidAccountsWithDataSets(String table) {
475443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Set<AccountWithDataSet> accountsWithDataSets = new HashSet<AccountWithDataSet>();
47555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().rawQuery(
475643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "SELECT DISTINCT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
475743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "," + RawContacts.DATA_SET +
475843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                " FROM " + table, null);
4759627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4760627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
476191abbc9f691297594262d1f2d79acb744a66712cDave Santoro                if (!c.isNull(0) && !c.isNull(1)) {
476243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSets.add(
476343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new AccountWithDataSet(c.getString(0), c.getString(1), c.getString(2)));
4764627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4765627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4766627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4767627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4768627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
476943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountsWithDataSets;
4770627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4771627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
47724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
47734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
47744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
477515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
477615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
477715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
477836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
477936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamReadPermission(uri);
478036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
47815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Query the profile DB if appropriate.
47825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
47835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
47845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mProfileProvider.query(uri, projection, selection, selectionArgs, sortOrder);
47855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
47865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
47875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Otherwise proceed with a normal query against the contacts DB.
47885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
47895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(mContactsHelper.getReadableDatabase());
4790d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4791385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
4792b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1));
4794385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
4795b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
47975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            Directory.DEFAULT));
4798d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
4799b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
48003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
48015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            Directory.LOCAL_INVISIBLE));
4802d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4803d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4804d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4805d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4806a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4807a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4808d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4809d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4810d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4811d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4812d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4813d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4814d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4815d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4816d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4817d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4818d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4819d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
48202e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
48212e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
48222e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
48232e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
48242e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
48252e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4826d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
482709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
482809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
482909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
483009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
483109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4832332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4833d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
48346ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
48356ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
48366ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
48376ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
48386ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4839547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
4840547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
4841b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri, cursor);
4842547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
4843b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return matrixCursorFromCursor(addSnippetExtrasToCursor(uri, cursor));
4844547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
48453716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
48463716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4847b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addSnippetExtrasToCursor(Uri uri, Cursor cursor) {
4848547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
4849547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
4850547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
4851b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return cursor;
4852547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
4853547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
48543716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
48553716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
48563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
48573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
48583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
48593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
48603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
48613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
48623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
48633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
48643716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
48653716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
48663716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
48673716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
48683716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
48693716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
48703716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
48713716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4872b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        // Snippet data is needed for the snippeting on the client side, so store it in the cursor
4873b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor && deferredSnippetingRequested(uri)){
4874b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4875b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4876b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4877b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4878b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4879b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putString(ContactsContract.DEFERRED_SNIPPETING_QUERY, query);
4880b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4881b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
48825517770250b3afa4fd88b6869c3244680821d222Dave Santoro        }
4883b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
4884b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
4885b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4886b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addDeferredSnippetingExtra(Cursor cursor) {
4887b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor){
4888b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4889b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4890b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4891b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4892b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4893b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putBoolean(ContactsContract.DEFERRED_SNIPPETING, true);
4894b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
4895b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
4896b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
48976ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
48986ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
48996ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
49006ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
49016ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
49026ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
49036ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
49046ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
49056ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
49066ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
49076ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
49086ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
49096ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
49106ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
49116ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
49126ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
49136ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
49146ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
49156ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
49166ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
49176ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
49186ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
49196ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
4920332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
49216ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
4922d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4923d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4924d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4925d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4926d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4927d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4928d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4929d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4930d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4931d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4932d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4933d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4934d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4935d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4936d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4937d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4938d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4939d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4940d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4941d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
49424458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
49434458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
49444458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
49455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
494649d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
49474458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
49484458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
49494458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
49504458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
49514458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
49524458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
49534458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
49544458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
49554458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
49564458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
49574458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
49584458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
49594458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4960d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
49614458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4962d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4963d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
49644458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
49654458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4966d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4967d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
496872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
49694458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
49704458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
49714458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
497272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
497372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
497423ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki    private boolean hasColumn(String[] projection, String column) {
497523ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki        if (projection == null) {
497623ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki            return true; // Null projection means "all columns".
497723ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki        }
497823ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki
497923ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki        for (int i = 0; i < projection.length; i++) {
498023ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki            if (column.equalsIgnoreCase(projection[i])) return true;
498123ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki        }
498223ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki        return false;
498323ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki    }
498423ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki
49855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    protected Cursor queryLocal(Uri uri, String[] projection, String selection,
49865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String[] selectionArgs, String sortOrder, long directoryId) {
4987bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4988bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4989bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
49900b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
49915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
49925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
4993078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getReadableDatabase());
49945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
499535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4996d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
49971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4998c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4999b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean snippetDeferred = false;
5000c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
50012ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // The expression used in bundleLetterCountExtras() to get count.
50022ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        String addressBookIndexerCountExpression = null;
50032ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
5004a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
50054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
500635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
50075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
50085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().query(mActiveDb.get(), projection, selection,
50095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs, sortOrder);
501035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
5011d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
5012763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
50134b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
5014619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
5015619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
5016619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5017d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
50184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
5019763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
50204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
50214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
50226bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
50236bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
50246bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
50255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
50265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
50275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
50285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
50295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
50305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
5031fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
50325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
5033a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
50345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
50355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
50365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
50375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
5038763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
5039a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
50405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5041a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5042a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
5043a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
50445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
50455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
50465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
50475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5048763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
50494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
50505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
50514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
50525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
50535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
50545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50552149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
5056bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_DATA:
5057bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
5058bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO: {
50592149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
50602149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
50612149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
50625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
50632149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
50642149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
50652149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
50662149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
50672149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
50682149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
50692149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
5070bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
5071bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
5072bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
5073a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
50745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5075a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5076a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
5077a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
50782149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
50792149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
50802149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50812149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
50822149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
50832149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50842149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
50862149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
508724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
5088bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
5089bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
5090bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                }
50912149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
50922149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
50932149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
50942149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
50963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
50973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
50983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5099af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                qb.appendWhere(StreamItems.CONTACT_ID + "=?");
51003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
51013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
51023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
51033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
51043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
51053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
51063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
51073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
51085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
51093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
51103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
51113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
51123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
51133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
51143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
51153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
51165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
51173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5118af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_ID, contactId,
5119af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_LOOKUP_KEY, lookupKey);
51203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
51213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
51223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
51233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
51243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
51253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
51265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
51273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
51283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
51293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
51303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
51313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5132f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
513342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
51345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
5135ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
5136f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
51374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
513824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
51394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
5140f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
5141f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
5142f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
514342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
514442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
514542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
51465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().rawQuery(
514742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
514842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
514942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
515042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
515142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
515242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
5153ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
5154916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
5155b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                boolean deferredSnipRequested = deferredSnippetingRequested(uri);
5156ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
5157916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
5158ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
51597ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
5160b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        qb, uri, projection, filterParam, directoryId,
5161b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        deferredSnipRequested);
5162b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                snippetDeferred = isSingleWordQuery(filterParam) &&
5163b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        deferredSnipRequested && snippetNeeded(projection);
5164ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5165ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5166ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
5167ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
5168ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
51692f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
51702f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
51712f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
51722f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
51732f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
51742f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
51752f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
51762f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
51772f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
51784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
51794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5180e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
51815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
51822f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
51834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
51844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
51852f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
51865e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
51872f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
51885e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
51895e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
51904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
51914928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
51924928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                qb.setProjectionMap(phoneOnly ?
51934928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        sStrequentPhoneOnlyStarredProjectionMap
51944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        : sStrequentStarredProjectionMap);
51959dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                if (phoneOnly) {
51965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    qb.appendWhere(DbQueryUtils.concatenateClauses(
51975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            selection, Contacts.HAS_PHONE_NUMBER + "=1"));
51989dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                }
51992f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
520072c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                final String starredInnerQuery = qb.buildQuery(subProjection,
520172c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                        Contacts.STARRED + "=1", Contacts._ID, null,
520272c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                        Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC", null);
5203d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
52042f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
5205d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
52062f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
52074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
520872c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                // Build the second query for frequent part. These JOINS can be very slow
520972c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                // if assembled in the wrong order. Be sure to test changes against huge databases.
521072c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                final String frequentInnerQuery;
52114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                if (phoneOnly) {
52124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    final StringBuilder tableBuilder = new StringBuilder();
52134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // In phone only mode, we need to look at view_data instead of
52144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // contacts/raw_contacts to obtain actual phone numbers. One problem is that
52154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data is much larger than view_contacts, so our query might become much
52164928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // slower.
52174928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    //
52184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // To avoid the possible slow down, we start from data usage table and join
52194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data to the table, assuming data usage table is quite smaller than
52204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // data rows (almost always it should be), and we don't want any phone
52214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // numbers not used by the user. This way sqlite is able to drop a number of
52224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // rows in view_data in the early stage of data lookup.
52234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    tableBuilder.append(Tables.DATA_USAGE_STAT
52244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
52254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
52264928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataColumns.CONCRETE_ID + " AND "
52274928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
52284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
52294928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
52304928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactStatusUpdateJoin(tableBuilder, projection,
52314928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            ContactsColumns.LAST_STATUS_UPDATE_ID);
52324928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
52334928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setTables(tableBuilder.toString());
52344928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
523572c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                    final long phoneMimeTypeId =
523672c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            mDbHelper.get().getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
523772c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                    final long sipMimeTypeId =
523872c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            mDbHelper.get().getMimeTypeId(SipAddress.CONTENT_ITEM_TYPE);
52394928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
52404928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
52414928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
524272c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            DataColumns.MIMETYPE_ID + " IN (" +
524372c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            phoneMimeTypeId + ", " + sipMimeTypeId + ")"));
524472c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                    frequentInnerQuery =
524572c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            qb.buildQuery(subProjection, null, null, null,
524672c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            TIMES_USED_SORT_COLUMN + " DESC", "25");
52474928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                } else {
52484928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
52494928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
52504928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
52514928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
52525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)"));
525372c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                    frequentInnerQuery = qb.buildQuery(subProjection,
525472c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                            null, Contacts._ID, null, null, "25");
52554928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                }
5256d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
525772c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                // We need to wrap the inner queries in an extra select, because they contain
525872c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                // their own SORT and LIMIT
525972c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                final String frequentQuery = "SELECT * FROM (" + frequentInnerQuery + ")";
526072c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                final String starredQuery = "SELECT * FROM (" + starredInnerQuery + ")";
526172c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann
5262d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
52632f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
526472c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery}, null, null);
52652f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
52662f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
52672f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
52682f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
52692f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
52702f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
52712f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
52722f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
52732f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
52742f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
52752f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
52762f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
52772f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
52787d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
52797d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
52802f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
52812f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
52825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = mActiveDb.get().rawQuery(unionQuery, doubledSelectionArgs);
52832f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
52842f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
5285d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
5286d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
52872f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
5288d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
5289d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
529045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            case CONTACTS_FREQUENT: {
529145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
529245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.setProjectionMap(sStrequentFrequentProjectionMap);
529345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                groupBy = Contacts._ID;
529445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                if (!TextUtils.isEmpty(sortOrder)) {
529545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
529645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                } else {
529745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY;
529845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                }
529945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                break;
530045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            }
530145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
5302ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
5303763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
5304b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
530571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
53067cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    String groupMimeTypeId = String.valueOf(
53077cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
53084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53097cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    selectionArgs = insertSelectionArg(selectionArgs, groupMimeTypeId);
5310b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
5311b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
5312b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
5313b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
531424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
531524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
531624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
531724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
531824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
531924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
532024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
532124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
532224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
532324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
532424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
5325ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
532624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
532724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
532824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
532924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5330a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
53314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
533282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
53344da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
53356bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
53366bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
533700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
5338a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
53393653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
534082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
53424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
53433653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
53443653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
53453653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
53463653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
5347a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
5348a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
5349a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5350a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5351a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
5352a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5353a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5354a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5355a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
5356a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
5357a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
5358a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
5359a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
53605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
5361a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
5362a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5363a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
5364a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
5365a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
5366a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
5367a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
5368a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
5369a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
53705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5371a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5372a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
5373a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
5374a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
5375a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
5376a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
5377a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5378a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5379a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5380a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
53815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
5382a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
5383a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5384a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5385a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
53863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
53873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
53883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
53923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
53933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53949b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                qb.appendWhere(StreamItems._ID + "=?");
53953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
53996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
54006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
54013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return cursor;
54023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
54033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
54043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
54053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
54063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
54073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
54083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
54093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
54103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
54113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
54123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
54133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
54143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
54153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
54163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
54173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
54183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
54193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
54203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
54213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
54223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
54233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
54243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
54253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
54263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
54273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5428f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case PHOTO_DIMENSIONS: {
5429f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                MatrixCursor cursor = new MatrixCursor(
5430f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
5431f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        1);
5432f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
5433f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return cursor;
5434f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
5435f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
54364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
543782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54387cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + "=" +
54397cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        mDbHelper.get().getMimeTypeIdForPhone());
54402ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
54418ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                final boolean removeDuplicates = readBooleanQueryParameter(
54428ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                        uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
54438ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                if (removeDuplicates) {
54448ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
54458ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
54468ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // In this case, because we dedupe phone numbers, the address book indexer needs
54478ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // to take it into account too.  (Otherwise headers will appear in wrong
54488ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // positions.)
54498ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // So use count(distinct pair(CONTACT_ID, PHONE NUMBER)) instead of count(*).
54508ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // But because there's no such thing as pair() on sqlite, we use
54518ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // CONTACT_ID || ',' || PHONE NUMBER instead.
54528ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // This only slows down the query by 14% with 10,000 contacts.
54538ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    addressBookIndexerCountExpression = "DISTINCT "
54548ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                            + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
54558ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                }
54562815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
54572815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
54582815f58f72f109790585931f601a63ddc02536a5Evan Millar
545948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
546082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
54627cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54637cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForPhone());
54644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
546548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
546648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
546748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5468ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
546946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
547046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
547146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
547246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
547346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
547446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
54757cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54767cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForPhone());
5477ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
54784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
54794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5480a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
54815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
548245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
54835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
5484d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    final String ftsMatchQuery = SearchIndexManager.getFtsMatchQuery(
5485d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                            filterParam, FtsQueryBuilder.UNSCOPED_NORMALIZING);
5486d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    if (ftsMatchQuery.length() > 0) {
5487155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
5488155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5489155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5490155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5491155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5492155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5493d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                                " WHERE " + SearchIndexColumns.NAME + " MATCH '");
5494d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append(ftsMatchQuery);
5495d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append("')");
54965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
549745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
54985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
54995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5500892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
5501892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
55025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
55035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
55045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
55055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
5506892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
5507892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
5508892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
5509892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
5510892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
551145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
551245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
551345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
551445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
551545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
551645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
551745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
55185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
55195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5520a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
5521ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
552258567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                groupBy = "(CASE WHEN " + PhoneColumns.NORMALIZED_NUMBER
552358567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " IS NOT NULL THEN " + PhoneColumns.NORMALIZED_NUMBER
552458567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " ELSE " + Phone.NUMBER + " END), " + RawContacts.CONTACT_ID;
5525a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
552646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
552746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
552846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
552946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
553046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
553146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
5532a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
5533ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5534ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5535ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
55364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
553782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55387cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55397cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
55408ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
55418ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                final boolean removeDuplicates = readBooleanQueryParameter(
55428ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                        uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
55438ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                if (removeDuplicates) {
55448ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
55458ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
55468ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // See PHONES for more detail.
55478ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    addressBookIndexerCountExpression = "DISTINCT "
55488ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                            + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
55498ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                }
55504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
55514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
55524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
555348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
555482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
55567cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55577cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail()
55584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
555948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
556048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
556148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
55625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
556382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55647cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55657cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
55664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
556708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
55685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    String address = mDbHelper.get().extractAddressFromEmailAddress(email);
556908768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
557008768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
55714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
5572071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                // unless told otherwise, we'll return visible before invisible contacts
5573071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                if (sortOrder == null) {
5574071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                    sortOrder = "(" + RawContacts.CONTACT_ID + " IN " +
5575071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                            Tables.DEFAULT_DIRECTORY + ") DESC";
5576071d0e7bd3b2f3c9628dd655b09d147e668c3931Daniel Lehmann                }
5577ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5578ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5579ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
55805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
558146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
558246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
558346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
558446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
558546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
558646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
558707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
55887d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
558907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
559007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
559107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
559207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
559307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
559407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
55955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
559607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
559707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
559807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
559907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
560007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
560107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
560207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
560307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
560407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
56052a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
56065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    sb.append(mDbHelper.get().getMimeTypeIdForEmail());
56072a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
560807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
560920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
5610155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
5611155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
5612155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
5613155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
56145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        sb.append(mDbHelper.get().getMimeTypeIdForEmail());
5615155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
5616155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5617155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5618155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5619155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5620155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5621d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                                " WHERE " + SearchIndexColumns.NAME + " MATCH '");
5622d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        final String ftsMatchQuery = SearchIndexManager.getFtsMatchQuery(
5623d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                                filterParam, FtsQueryBuilder.UNSCOPED_NORMALIZING);
5624d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append(ftsMatchQuery);
5625d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                        sb.append("')");
56265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
56275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5628a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
56295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
56305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
5631a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
563246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
563346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
563446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
56357d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
56367d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
56377d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
5638a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
56395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
56405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
56415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5642ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
564382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56447cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
56457cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
56468ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
56478ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                final boolean removeDuplicates = readBooleanQueryParameter(
56488ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                        uri, ContactsContract.REMOVE_DUPLICATE_ENTRIES, false);
56498ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                if (removeDuplicates) {
56508ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
56518ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa
56528ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    // See PHONES for more detail.
56538ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                    addressBookIndexerCountExpression = "DISTINCT "
56548ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                            + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
56558ead0dc62d0031a22af0d14c7ed05893507893c9Daisuke Miyakawa                }
5656ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5657ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5658ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
565948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
566082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
56627cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
56637cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
56644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
566548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
566648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
566748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5668d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
5669d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
5670763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
56714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
56724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
56734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5674d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
5675d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
56765ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
5677763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
56784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
56804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
56814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
56824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5683d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
5684d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
5685d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
5686d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(segment));
568782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
569024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
569124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
569224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
56933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
56943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
56953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
56963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
56983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
56993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
570024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
570182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
570282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
570382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long streamItemId = Long.parseLong(uri.getPathSegments().get(3));
570482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                setTablesAndProjectionMapForStreamItems(qb);
570582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(streamItemId));
5706c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
570782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=? AND " +
570882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems._ID + "=?");
570982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
571082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
571182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
571224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
571324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
571424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
571524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
57165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                qb.appendWhere(" AND " + RawContacts._ID + "=?");
5717e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5718e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5719e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5720d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA:
5721d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA: {
572282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
5723e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5724e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5725e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5726d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA_ID:
5727d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
572882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
57294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
57304da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
5731a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
5732a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
5733a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
573485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            case PROFILE_PHOTO: {
573585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
573685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
573785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                break;
573885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            }
573985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro
5740a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
5741e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
5742e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
5743e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
574458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                if (uri.getBooleanQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, false)) {
574558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    if (TextUtils.isEmpty(sortOrder)) {
574658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // Default the sort order to something reasonable so we get consistent
574758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // results when callers don't request an ordering
574858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        sortOrder = Contacts.DISPLAY_NAME + " ASC";
574958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    }
575058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
575158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String sipAddress = uri.getPathSegments().size() > 1
575258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            ? Uri.decode(uri.getLastPathSegment()) : "";
575358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    setTablesAndProjectionMapForData(qb, uri, null, false, true);
575458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    StringBuilder sb = new StringBuilder();
575558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    selectionArgs = mDbHelper.get().buildSipContactQuery(sb, sipAddress);
575658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    selection = sb.toString();
575758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                } else {
575858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    if (TextUtils.isEmpty(sortOrder)) {
575958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // Default the sort order to something reasonable so we get consistent
576058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        // results when callers don't request an ordering
576158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                        sortOrder = " length(lookup.normalized_number) DESC";
576258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    }
576358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
576458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String number = uri.getPathSegments().size() > 1
576558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            ? uri.getLastPathSegment() : "";
576658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
576758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            mDbHelper.get().getCurrentCountryIso());
576858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    String normalizedNumber =
576958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            PhoneNumberUtils.normalizeNumber(number);
577058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    mDbHelper.get().buildPhoneLookupAndContactQuery(
577158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                            qb, normalizedNumber, numberE164);
577258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    qb.setProjectionMap(sPhoneLookupProjectionMap);
577358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                }
5774a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
5775a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
5776a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5777ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
5778ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5779ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
5780f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5781ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5782ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5783ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5784ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
5785ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5786ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
57874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
57884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
5789ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5790ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5791ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5792ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
5793f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                final boolean returnGroupCountPerAccount =
5794f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
5795f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                                false);
579623ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                String tables = Views.GROUPS + " AS " + Tables.GROUPS;
579723ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                if (hasColumn(projection, Groups.SUMMARY_COUNT)) {
579823ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                    tables = tables + Joins.GROUP_MEMBER_COUNT;
579923ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                }
580023ba865a6d204ba4aa29d2fad9989e9c44351e81Makoto Onuki                qb.setTables(tables);
5801f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setProjectionMap(returnGroupCountPerAccount ?
5802f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        sGroupsSummaryProjectionMapWithGroupCountPerAccount
5803f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        : sGroupsSummaryProjectionMap);
5804f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5805f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                groupBy = GroupsColumns.CONCRETE_ID;
5806ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5807ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5808ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5809b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
58100c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
5811b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
5812b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
5813b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
5814b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
581531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
5816d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
58172d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
58182d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
58192d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
58202d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
582131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
5822d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
5823d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
582431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
582531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
582631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
582731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
58285b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
58295b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
58305b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
58315b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
58325b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
58335b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
58345b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
58355b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
583676dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
58375b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
58385b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
58395b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
58405b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
58415b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
58425b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
58435b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5844763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
58457581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
58465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mAggregator.get().queryAggregationSuggestions(qb, projection, contactId,
58475b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
584831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
584931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5850eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5851eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5852eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
5853f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5854e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5855e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5856e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
58575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final String groupMembershipMimetypeId = Long.toString(mDbHelper.get()
5858e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
585982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
58605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5861e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5862e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
586382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
58645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(
58655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                                projection, Settings.UNGROUPED_WITH_PHONES)) {
5866e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5867e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5868e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5869eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5870eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5871eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
58725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
58735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
58740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
58755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
58765ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
58775ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
587882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
58790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
58804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
58814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
58825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
58835ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
58845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5885c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5886174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
58875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), uri, projection, limit);
5888c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5889c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5890c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
58912d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
5892174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
5893174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
5894174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
58955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), projection, lookupKey, filter);
5896c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5897c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
58983202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case RAW_CONTACT_ENTITIES:
58993202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case PROFILE_RAW_CONTACT_ENTITIES: {
5900a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
590146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
590246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
590346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
590446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
590546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5906a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
59074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
59084da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
590946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
591046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
591146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
591209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
591309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
591409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
591509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5916d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5917d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5918d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5919d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5920d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5921d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5922d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5923385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5924d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5925d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5926385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5927d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5928d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5929d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5930d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
59317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
59327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
59337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
59347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
59354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5936f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5937c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
59384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
59394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
594009e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
59417f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
5942ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
59435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                query(mActiveDb.get(), qb, projection, selection, selectionArgs, sortOrder, groupBy,
59445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        limit);
5945ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
59465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = bundleLetterCountExtras(cursor, mActiveDb.get(), qb, selection,
59472ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                    selectionArgs, sortOrder, addressBookIndexerCountExpression);
5948ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5949b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetDeferred) {
5950b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            cursor = addDeferredSnippetingExtra(cursor);
5951b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
5952ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
59535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
59545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
59555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
59565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
59575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5958038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5959038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5960038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5961038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
59625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
59635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
59644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
59654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
59664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
59674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
59684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
59694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
597009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
597109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
597209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
597309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
597409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
597509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
597609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
597709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
597809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
597909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
598009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
598109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
598209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
598309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
598409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
598509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5986a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5987a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5988a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5989a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5990a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5991a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5992a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5993a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5994a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5995a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5996a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5997a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5998a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5999a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
6000a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
6001a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
6002a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6003a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
6004a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
6005a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
6006a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
6007a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
6008a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
6009a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
6010a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6011a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6012a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
6013a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
6014a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
601509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
6016bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
6017bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
6018bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
6019bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
6020ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6021bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
6022bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
6023ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
6024ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6025bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
6026bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
6027bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
6028bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
60295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // The first letter of the sort key column is what is used for the index headings.
60305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        public static final String SECTION_HEADING = "SUBSTR(%1$s,1,1)";
603124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
6032de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
6033ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
6034ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6035ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
6036ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
6037ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
6038ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
6039ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
60402ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder,
60412ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            String countExpression) {
6042409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki        if (!(cursor instanceof AbstractCursor)) {
6043409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            Log.w(TAG, "Unable to bundle extras.  Cursor is not AbstractCursor.");
6044409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            return cursor;
6045409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki        }
6046ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
6047ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6048ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
6049ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
6050ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
6051ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
6052ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
6053ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
6054ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
6055ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
6056ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
6057ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
6058ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
6059ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
6060ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
6061ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
6062ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
6063ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6064bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
6065ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
6066a99ffbd887e5120951845e5c60c32f459f71e9f2Makoto Onuki        String sectionHeading = String.format(Locale.US, AddressBookIndexQuery.SECTION_HEADING,
6067a99ffbd887e5120951845e5c60c32f459f71e9f2Makoto Onuki                sortKey);
6068bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
606924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
6070bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
60712ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // If "what to count" is not specified, we just count all records.
60722ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        if (TextUtils.isEmpty(countExpression)) {
60732ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            countExpression = "*";
60742ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        }
60752ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
6076bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
6077bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
6078bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
6079bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
6080bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
6081bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
6082bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
6083ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
608424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
6085bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
6086ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
60872ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                "COUNT(" + countExpression + ") AS " + AddressBookIndexQuery.COUNT);
6088ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
6089ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6090f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
6091ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
6092ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
6093ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6094ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
6095f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
6096ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
6097ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
6098bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
6099bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
6100bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6101bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
6102bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
6103bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
6104ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
6105f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
6106bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
6107bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
6108bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
6109bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
6110bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
6111bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
6112bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
6113bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
6114bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
6115bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
6116bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6117bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
6118bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
6119bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
6120bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
6121bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6122bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
6123bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
6124bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
6125ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
6126ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6127409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            final Bundle bundle = new Bundle();
6128409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
6129409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
6130409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki
6131409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            ((AbstractCursor) cursor).setExtras(bundle);
6132409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            return cursor;
6133ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
6134f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
6135ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
6136ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
6137ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
61382d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
613992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
614092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
614192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
614292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
61432d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
61442d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
61455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
61465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
61475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
614892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
61495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_PROFILE)) {
61505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // We should already be in a profile database context, so just look up a single contact.
61515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro           contactId = lookupSingleContactId(db);
61525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
61535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
615492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
615592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
615692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
615792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
615892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
615992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
616092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
616192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
616292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
616392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
616492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
616592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
616692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
616792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
616892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
616992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
617092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
617192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
61725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
61735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
61765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
61775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long lookupSingleContactId(SQLiteDatabase db) {
61795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = db.query(Tables.CONTACTS, new String[] {Contacts._ID},
61805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                null, null, null, null, null, "1");
61815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        try {
61825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (c.moveToFirst()) {
61835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return c.getLong(0);
61845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            } else {
61855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return -1;
61865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
61875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } finally {
61885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c.close();
61895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
61905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
61915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
61925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
619343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
61945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
61965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
619743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
61985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
61995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
62005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
62015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
620343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
62045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
62055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
62065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
62095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
62105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
62115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
62125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
62135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
621492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
62155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
62165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
62175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
62205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
62215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
62235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
62245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
62255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
622643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
622743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
62285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
62295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
623043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
62315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
62325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
62335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
623492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
623592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
62365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
62375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
62385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
62395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
62405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
62415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
62435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
62445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
62475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
624992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
625043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
62515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
62535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
625443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
62555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
625692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
62575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
62585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
626043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
62615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
626292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
62635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
626592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
626692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
626792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
626892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
62695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
62705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
627192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
627292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
627392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
62745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
627692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
627792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
62785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
627992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
628092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
628192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
628292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
628343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet = c.getString(
628443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        LookupByRawContactIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
628592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
628692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
628743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
628892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
628992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
629092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
629192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
629292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
629392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
629492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
629592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
629692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
629792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
629892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
629992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
630092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
63015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
630392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
630492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
630592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
630692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
630792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
630892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
630992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
631092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
631143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
631292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
631392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
631492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
631592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
631692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
631743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
631892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
631992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
632092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
632192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
632292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
632392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
63245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
63255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
63265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
63275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
632892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
632992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
63305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
63315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
63325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
63335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
63355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
63365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
63375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
63395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
63405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
63415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
634243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
634343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET);
63445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
63455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
634643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
63475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
63485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
63495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
635092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
635192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
635292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
63535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
63545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
63555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
63565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
63575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
63585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
63595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
63605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
63615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
63645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
63655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
636692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
636792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
636892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
636992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
637092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
637192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
637292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
637392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
637492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
637592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
637692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
6377ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
63785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().updateLookupKeyForRawContact(db, rawContactId);
6379ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
6380ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
63815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
63825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
63835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
63845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
63855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
63865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
63885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
63895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
63915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
63925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
63945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
63955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
63965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
63975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
63985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
63995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
64005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
64015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
64025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
64035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
64045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
64055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
64065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
64075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
64085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
64095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
64105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
64115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
64125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
64135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
64145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
64155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
6416763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
6417763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
64184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false);
64192f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
64202f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
64212f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
64224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * @param includeDataUsageStat true when the table should include DataUsageStat table.
64234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Note that this uses INNER JOIN instead of LEFT OUTER JOIN, so some of data in Contacts
64244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * may be dropped.
64252f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
64262f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
64274928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            String[] projection, boolean includeDataUsageStat) {
642882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
642972c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann        if (includeDataUsageStat) {
643072c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann            sb.append(Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT);
643172c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann            sb.append(" INNER JOIN ");
643272c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann        }
643372c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann
6434ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
64352f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
64362f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
64374928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        if (includeDataUsageStat) {
643872c4b2612a06636343e2803c0c84fdfbd5ba2f63Daniel Lehmann            sb.append(" ON (" +
64392f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
64402f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
64414928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID) +
64422f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
64432f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
64442f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
64457ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
64467ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6447916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
6448916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
6449916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
6450916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6451916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
6452916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
6453916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
6454916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
6455916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
6456b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            String[] projection, String filter, long directoryId, boolean deferredSnippeting) {
64577ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
64587ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6459ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
6460916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
646103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
646203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
646303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
646403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
646530cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
646630cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
64675e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
6468b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, uri, projection, filter, deferredSnippeting);
64695e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
64707ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
64717ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
647203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
647303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
647403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
6475916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
647603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
6477b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            StringBuilder sb, Uri uri, String[] projection, String filter,
6478b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            boolean  deferredSnippeting) {
6479916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6480b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetNeeded(projection)) {
648103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
648203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
648303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
648403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
648503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
648603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
648703197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
64885e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
64895e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
64905e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
64915e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
64925e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
64935e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
64945e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
64955e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
64965e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6497174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
6498b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens,
6499b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    deferredSnippeting);
6500174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6501b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, filter, false, null, null, null, 0, false);
6502174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6503174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
6504174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6505174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
6506174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
6507b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            int maxTokens, boolean deferredSnippeting) {
6508174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
6509174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
6510174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
6511174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
6512174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
6513174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
65143716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
65153716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
6516b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean singleTokenSearch = isSingleWordQuery(filter);
65173716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6518174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
65195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            emailAddress = mDbHelper.get().extractAddressFromEmailAddress(filter);
6520174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
6521174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6522174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
652304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
652404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
652504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
65265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().getCountryIso());
652704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
6528174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6529174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6530d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        final String SNIPPET_CONTACT_ID = "snippet_contact_id";
6531d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS " + SNIPPET_CONTACT_ID);
6532174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
65335e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
65345e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
65353d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
65365e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
653704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
653804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
653904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
654004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
654104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
654204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
65433d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
65443d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
65453716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6546b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                // Optimization for single-token search (do only if requested).
6547b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                if (singleTokenSearch && deferredSnippeting) {
65483716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
65493716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
65503716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
65513716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
65523d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
65533d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
65543d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
65553d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
655604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
655704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
655804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
655904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
656004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
656104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
656204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
656304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
656404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
656504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
656604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
656704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
656804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
656904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
657004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
657104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
65725e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
65735e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
65743716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6575b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                // Optimization for single-token search (do only if requested).
6576b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                if (singleTokenSearch && deferredSnippeting) {
65773716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
65783716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
65793716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
65803716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
65815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
658203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
658304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
658404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
6585b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    // Optimization for single-token search (do only if requested)..
6586b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    if (singleTokenSearch && deferredSnippeting) {
65873716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
65883716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
65893716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
65903716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
65913716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
65923716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
65933716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
65943716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
65953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
65963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
65973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
65983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
65993716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
66003716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
66013716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
66023716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
660304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
660404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
660504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
660603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
66075e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
66085e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
660903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
66105e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
66115e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
6612d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        sb.append(Tables.SEARCH_INDEX + " MATCH '");
66135e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
6614d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // we know that the emailAddress contains a @. This phrase search should be
6615d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // scoped against "content:" only, but unfortunately SQLite doesn't support
6616d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // phrases and scoped columns at once. This is fine in this case however, because:
6617d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            //  - We can't erronously match against name, as name is all-hex (so the @ can't match)
6618d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            //  - We can't match against tokens, because phone-numbers can't contain @
6619d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String sanitizedEmailAddress =
6620d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    emailAddress == null ? "" : sanitizeMatch(emailAddress);
6621d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append("\"");
6622d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append(sanitizedEmailAddress);
6623d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append("*\"");
66243d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
6625d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // normalized version of the phone number (phoneNumber can only have + and digits)
6626d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String phoneNumberCriteria = " OR tokens:" + phoneNumber + "*";
6627d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann
6628d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // international version of this number (numberE164 can only have + and digits)
6629d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String numberE164Criteria =
6630d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    (numberE164 != null && !TextUtils.equals(numberE164, phoneNumber))
6631d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    ? " OR tokens:" + numberE164 + "*"
6632d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    : "";
6633d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann
6634d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // combine all criteria
6635d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            final String commonCriteria =
6636d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    phoneNumberCriteria + numberE164Criteria;
6637d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann
6638d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // search in content
6639d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append(SearchIndexManager.getFtsMatchQuery(filter,
6640d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    FtsQueryBuilder.getDigitsQueryBuilder(commonCriteria)));
664103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
6642d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            // general case: not a phone number, not an email-address
6643d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann            sb.append(SearchIndexManager.getFtsMatchQuery(filter,
6644d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann                    FtsQueryBuilder.SCOPED_NAME_NORMALIZING));
66459c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
66461322df8f90d80587748ad10539516635326c01e8Daniel Lehmann        // Omit results in "Other Contacts".
66471322df8f90d80587748ad10539516635326c01e8Daniel Lehmann        sb.append("' AND " + SNIPPET_CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY + ")");
66481322df8f90d80587748ad10539516635326c01e8Daniel Lehmann        sb.append(" ON (" + Contacts._ID + "=" + SNIPPET_CONTACT_ID + ")");
6649a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
6650a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
6651d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann    private static String sanitizeMatch(String filter) {
6652d1746e09bc7739f3d1449cececc66d5045ada498Daniel Lehmann        return filter.replace("'", "").replace("*", "").replace("-", "").replace("\"", "");
66532352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
66542352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
66555e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
66565e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
66575e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
66585e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
66595e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
66605e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
66615e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
66625e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
66635e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
66645e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
66655e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
66665e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
66675e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
66685e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
66695e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6670763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
6671763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
6672ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
6673763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
6674763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
6675f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6676763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
6677763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
6678a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
6679ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
6680a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
6681f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
668246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
668346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
668482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
668582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
668658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        setTablesAndProjectionMapForData(qb, uri, projection, distinct, false, null);
668758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    }
668858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
668958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
669058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            String[] projection, boolean distinct, boolean addSipLookupColumns) {
669158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        setTablesAndProjectionMapForData(qb, uri, projection, distinct, addSipLookupColumns, null);
669246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
669346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
669446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
669546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
669646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
669746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
669846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
669946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
670058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        setTablesAndProjectionMapForData(qb, uri, projection, distinct, false, usageType);
670158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    }
670258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
670358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
670458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            String[] projection, boolean distinct, boolean addSipLookupColumns, Integer usageType) {
670582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6706ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
670782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
670882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
6709a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
6710a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6711a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6712a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
67133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
671446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
671546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
671646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
671746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
671882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
6719f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
6720f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
67215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                || !mDbHelper.get().isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
6722f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
672358795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
672458795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        final ProjectionMap projectionMap;
672558795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        if (addSipLookupColumns) {
672658795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            projectionMap = useDistinct
672758795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda                    ? sDistinctDataSipLookupProjectionMap : sDataSipLookupProjectionMap;
672858795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        } else {
672958795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda            projectionMap = useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap;
673058795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        }
673158795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda
673258795e447fada97b9594bd7ba2e3dca241487d01Flavio Lerda        qb.setProjectionMap(projectionMap);
6733f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6734ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
6735ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
67360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
67370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
67380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6739ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
67400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
6741a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6742a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
67430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6744a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6745a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
6746a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6747a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
67483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
67499b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann        qb.setTables(Views.STREAM_ITEMS);
67503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
67513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
67523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
67533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
67541dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.PHOTO_FILES
67551dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.STREAM_ITEM_PHOTOS + " ON ("
67561dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_PHOTO_FILE_ID + "="
67571dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + PhotoFilesColumns.CONCRETE_ID
67581dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.STREAM_ITEMS + " ON ("
67591dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "="
67600bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_ID + ")"
67610bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
67620bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
67630bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + ")");
67643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
67653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
67663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
6767a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
6768a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
6769a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6770ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
6771a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
6772a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6773a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
6774a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6775a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
6776a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
6777a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6778a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6779a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
6780f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6781a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6782a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6783a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
6784a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
67855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6786a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
6787a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
6788a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
6789a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
6790a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
6791a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
6792a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
6793a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
6794a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
67950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6796a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
67970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6798a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
6799a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
68005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
68010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
68020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
68030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
68040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
68050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
68060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
6807a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
6808a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
68090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6810a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6811a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
681246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
681346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
681446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
681546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
681646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
681746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
6818a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
6819a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
68205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6821a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
6822a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
6823a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
6824a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
6825a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6826a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6827a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6828a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
6829a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
68305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
6831a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
6832a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
6833a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6834a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6835a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
683624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
6837385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
6838385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
683924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
6840385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
6841385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
684224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
684324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
684424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
684524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
684624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
6847f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
6848f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6849f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
685043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6851e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6852e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6853e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6854e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
68555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6856fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6857e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6858e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6859e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6860e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6861e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6862e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
686343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String toAppend = RawContacts.ACCOUNT_NAME + "="
68644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
68654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
686643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + DatabaseUtils.sqlEscapeString(accountType);
6867f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (dataSet == null) {
6868f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + " IS NULL";
6869f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
6870f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + "=" +
6871f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                        DatabaseUtils.sqlEscapeString(dataSet);
687243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
687343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            qb.appendWhere(toAppend);
68744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
68754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
68764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
68774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
68784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
6879e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
6880f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6881f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
688243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6883e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6884e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6885e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6886e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
68875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6888fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6889e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6890e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6891e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6892e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6893e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6894e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
6895e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
6896e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
6897e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
6898e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
6899f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (dataSet == null) {
6900f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + " IS NULL");
6901f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
690243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + "=")
690343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        .append(DatabaseUtils.sqlEscapeString(dataSet));
690443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
6905e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
6906e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
6907e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
6908e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
6909e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
6910e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
6911e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
6912e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
6913e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
6914e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
6915e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
69167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
6917c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
6918c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
6919c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
6920c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
6921c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
6922f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
69232e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
6924c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
6925c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6926c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6927c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
6928c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
6929c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
6930c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
6931c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
6932c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
6933c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
6934c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
6935c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
6936c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
6937c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6938c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6939c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6940c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
6941b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
6942f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
6943f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (mode.equals("r")) {
6944f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mReadAccessLatch);
6945f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6946f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mWriteAccessLatch);
6947f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
69485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
69495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
69505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mProfileProvider.openAssetFile(uri, mode);
69515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
69525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
69535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return openAssetFileLocal(uri, mode);
69545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
69555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
69565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
69575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public AssetFileDescriptor openAssetFileLocal(Uri uri, String mode)
69585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throws FileNotFoundException {
69595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
69605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
69615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
6962078f588cef389358adabc579de00747878f3c108Dave Santoro            if (mode.equals("r")) {
6963078f588cef389358adabc579de00747878f3c108Dave Santoro                mActiveDb.set(mContactsHelper.getReadableDatabase());
6964078f588cef389358adabc579de00747878f3c108Dave Santoro            } else {
6965078f588cef389358adabc579de00747878f3c108Dave Santoro                mActiveDb.set(mContactsHelper.getWritableDatabase());
6966078f588cef389358adabc579de00747878f3c108Dave Santoro            }
69675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
6968415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6969b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
6970b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
6971a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
6972bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
69735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
697424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
697524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
6976bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        new String[]{String.valueOf(contactId)});
6977e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
6978b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6979f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO: {
6980f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6981f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6982f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact ID can only be read.");
6983f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6984f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
69855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
6986f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{Contacts.PHOTO_FILE_ID},
6987f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
6988f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, null);
6989f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
699085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    if (c.moveToFirst()) {
699185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        long photoFileId = c.getLong(0);
699285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        return openDisplayPhotoForRead(photoFileId);
699385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    } else {
699485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        // No contact for this ID.
699585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        throw new FileNotFoundException(uri.toString());
699685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    }
699785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                } finally {
699885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    c.close();
699985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                }
700085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            }
700185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro
700285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            case PROFILE_DISPLAY_PHOTO: {
700385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                if (!mode.equals("r")) {
700485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    throw new IllegalArgumentException(
700585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                            "Display photos retrieved by contact ID can only be read.");
700685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                }
700785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
700885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        new String[]{Contacts.PHOTO_FILE_ID}, null, null, null, null, null);
700985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                try {
701085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    if (c.moveToFirst()) {
701185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        long photoFileId = c.getLong(0);
701285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        return openDisplayPhotoForRead(photoFileId);
701385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    } else {
701485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        // No profile record.
701585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        throw new FileNotFoundException(uri.toString());
701685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    }
7017f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
7018f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
7019f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7020f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7021f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7022bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
7023bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
7024f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
7025f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO: {
7026f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
7027f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
7028bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            "Photos retrieved by contact lookup key can only be read.");
7029f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7030f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                List<String> pathSegments = uri.getPathSegments();
7031f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                int segmentCount = pathSegments.size();
7032f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount < 4) {
70335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
7034f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Missing a lookup key", uri));
7035f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7036bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro
7037bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                boolean forDisplayPhoto = (match == CONTACTS_LOOKUP_ID_DISPLAY_PHOTO
7038bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        || match == CONTACTS_LOOKUP_DISPLAY_PHOTO);
7039f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String lookupKey = pathSegments.get(2);
7040bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                String[] projection = new String[]{Contacts.PHOTO_ID, Contacts.PHOTO_FILE_ID};
7041f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount == 5) {
7042f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long contactId = Long.parseLong(pathSegments.get(3));
7043f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
7044f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
70455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
7046f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            projection, null, null, null, null, null,
7047f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
7048f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c != null) {
7049f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        try {
7050f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.moveToFirst();
7051bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            if (forDisplayPhoto) {
7052bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoFileId =
7053bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
7054bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openDisplayPhotoForRead(photoFileId);
7055bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            } else {
7056bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
7057bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
7058bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        Data._ID + "=?", new String[]{String.valueOf(photoId)});
7059bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            }
7060f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        } finally {
7061f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.close();
7062f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7063f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7064f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7065f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7066f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
7067f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
70685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
70695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection, Contacts._ID + "=?",
7070f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(contactId)}, null, null, null);
7071f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
7072f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
7073bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (forDisplayPhoto) {
7074bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
7075bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openDisplayPhotoForRead(photoFileId);
7076bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    } else {
7077bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
7078bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openPhotoAssetFile(mActiveDb.get(), uri, mode,
7079bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                Data._ID + "=?", new String[]{String.valueOf(photoId)});
7080bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
7081f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
7082f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
7083f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7084f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7085f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7086f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO: {
7087f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
7088f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                boolean writeable = !mode.equals("r");
7089f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7090f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Find the primary photo data record for this raw contact.
7091f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
7092f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
7093f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
70947cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
70955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection,
70967cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data.RAW_CONTACT_ID + "=? AND " + DataColumns.MIMETYPE_ID + "=?",
70977cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        new String[]{String.valueOf(rawContactId), String.valueOf(photoMimetypeId)},
7098f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, Data.IS_PRIMARY + " DESC");
7099f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long dataId = 0;
7100f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = 0;
7101f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
7102f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c.getCount() >= 1) {
7103f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        c.moveToFirst();
7104f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        dataId = c.getLong(0);
7105f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        photoFileId = c.getLong(1);
7106f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7107f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
7108f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
7109f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7110f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7111f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // If writeable, open a writeable file descriptor that we can monitor.
7112f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // When the caller finishes writing content, we'll process the photo and
7113f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // update the data record.
7114f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (writeable) {
7115f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForWrite(rawContactId, dataId, uri, mode);
7116f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } else {
7117f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
7118f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7119f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7120f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7121f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO: {
7122f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = ContentUris.parseId(uri);
7123f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
7124f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
7125f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by key can only be read.");
7126f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7127f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return openDisplayPhotoForRead(photoFileId);
7128f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7129f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7130e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
713124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
71327cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
71335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
71347cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data._ID + "=? AND " + DataColumns.MIMETYPE_ID + "=" + photoMimetypeId,
713524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
7136d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7137d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7138fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
7139fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
7140fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
7141fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
7142fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7143fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
7144fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
7145fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
714642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
7147fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
714842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
714942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
715042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
715142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7152fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
7153f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
715442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
715542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
715642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
715742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
715842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
715942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
7160fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
716142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
7162fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
7163d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
7164d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
716542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
716642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
7167d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
716842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
7169d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
717042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
71715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // TODO: Figure out what to do if the profile contact is in the list.
71725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
717324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
717442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
717542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
717642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
717742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
7178d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7179d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
7180d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
7181d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
7182d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7183fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
7184f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
7185d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7186b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7187b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
71885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new FileNotFoundException(mDbHelper.get().exceptionMessage(
71895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        "File does not exist", uri));
7190b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
7191b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
7192b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7193afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private AssetFileDescriptor openPhotoAssetFile(SQLiteDatabase db, Uri uri, String mode,
7194afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            String selection, String[] selectionArgs)
7195e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
7196e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
71975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new FileNotFoundException(mDbHelper.get().exceptionMessage("Mode " + mode
7198e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
7199e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
7200e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7201e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
7202ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
7203e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
720408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
7205f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7206f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
720708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
720808ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
720908ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
721008ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
7211e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
7212e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7213f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7214f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a display photo from the photo store for reading.
7215f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoFileId The display photo file ID
7216f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor that allows the file to be read.
7217f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @throws FileNotFoundException If no photo file for the given ID exists.
7218f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7219f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForRead(long photoFileId)
7220f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throws FileNotFoundException {
72215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        PhotoStore.Entry entry = mPhotoStore.get().get(photoFileId);
7222f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
7223d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            try {
7224d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                return makeAssetFileDescriptor(
7225d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        ParcelFileDescriptor.open(new File(entry.path),
7226d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                ParcelFileDescriptor.MODE_READ_ONLY),
7227d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        entry.size);
7228d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } catch (FileNotFoundException fnfe) {
7229d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
7230d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                throw fnfe;
7231d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            }
7232f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
7233f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
7234f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throw new FileNotFoundException("No photo file found for ID " + photoFileId);
7235f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7236f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7237f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7238f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7239f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a file descriptor for a photo to be written.  When the caller completes writing
7240f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to the file (closing the output stream), the image will be parsed out and processed.
7241f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If processing succeeds, the given raw contact ID's primary photo record will be
7242f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * populated with the inserted image (if no primary photo record exists, the data ID can
7243f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * be left as 0, and a new data record will be inserted).
7244f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rawContactId Raw contact ID this photo entry should be associated with.
7245f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param dataId Data ID for a photo mimetype that will be updated with the inserted
7246f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     image.  May be set to 0, in which case the inserted image will trigger creation
7247f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     of a new primary photo image data row for the raw contact.
7248f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param uri The URI being used to access this file.
7249f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param mode Read/write mode string.
7250f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor the caller can use to write an image file for the
7251f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     raw contact.
7252f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7253f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForWrite(long rawContactId, long dataId, Uri uri,
7254f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            String mode) {
7255f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
7256c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            ParcelFileDescriptor[] pipeFds = ParcelFileDescriptor.createPipe();
7257c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            PipeMonitor pipeMonitor = new PipeMonitor(rawContactId, dataId, pipeFds[0]);
7258c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            pipeMonitor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) null);
7259c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return new AssetFileDescriptor(pipeFds[1], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
7260f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } catch (IOException ioe) {
7261f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.e(TAG, "Could not create temp image file in mode " + mode);
7262f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return null;
7263f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7264f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7265f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7266f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7267c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * Async task that monitors the given file descriptor (the read end of a pipe) for
7268c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * the writer finishing.  If the data from the pipe contains a valid image, the image
7269c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * is either inserted into the given raw contact or updated in the given data row.
7270f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7271c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro    private class PipeMonitor extends AsyncTask<Object, Object, Object> {
7272c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private final ParcelFileDescriptor mDescriptor;
7273f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mRawContactId;
7274f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mDataId;
7275c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private PipeMonitor(long rawContactId, long dataId, ParcelFileDescriptor descriptor) {
7276f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mRawContactId = rawContactId;
7277f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mDataId = dataId;
7278c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            mDescriptor = descriptor;
7279f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7280f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7281f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        @Override
7282c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        protected Object doInBackground(Object... params) {
7283c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            AutoCloseInputStream is = new AutoCloseInputStream(mDescriptor);
7284f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
7285c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                Bitmap b = BitmapFactory.decodeStream(is);
7286f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (b != null) {
7287fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann                    waitForAccess(mWriteAccessLatch);
7288f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    PhotoProcessor processor = new PhotoProcessor(b, mMaxDisplayPhotoDim,
7289f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            mMaxThumbnailPhotoDim);
7290f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7291f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Store the compressed photo in the photo store.
72925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PhotoStore photoStore = ContactsContract.isProfileId(mRawContactId)
72935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            ? mProfilePhotoStore
72945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            : mContactsPhotoStore;
72955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long photoFileId = photoStore.insert(processor);
7296f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7297c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // Depending on whether we already had a data row to attach the photo
7298c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // to, do an update or insert.
7299f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (mDataId != 0) {
7300f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Update the data record with the new photo.
7301f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues updateValues = new ContentValues();
7302f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7303f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7304f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7305f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7306f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7307f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            updateValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7308f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7309f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7310c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                        update(ContentUris.withAppendedId(Data.CONTENT_URI, mDataId),
7311c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                                updateValues, null, null);
7312f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    } else {
7313f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Insert a new primary data record with the photo.
7314f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues insertValues = new ContentValues();
7315f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7316f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7317f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7318f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7319f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7320f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.IS_PRIMARY, 1);
7321f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7322f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            insertValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7323f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7324f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7325f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insert(RawContacts.CONTENT_URI.buildUpon()
7326f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(String.valueOf(mRawContactId))
7327f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(RawContacts.Data.CONTENT_DIRECTORY).build(),
7328f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                insertValues);
7329f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7330c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro
7331f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7332c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            } catch (IOException e) {
7333c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                throw new RuntimeException(e);
7334f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7335c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return null;
7336f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7337f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7338f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7339d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
7340d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7341d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7342f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
7343d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
7344d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7345f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
7346d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
7347d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
7348d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7349d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
7350d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7351f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7352f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
7353f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
7354d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
7355ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
7356ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
7357d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7358d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7359d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7360f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
7361f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
7362f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7363f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7364f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
7365f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
7366f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7367f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7368d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7369d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
7370d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
7371d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
7372d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7373fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
7374fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
7375d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
7376dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
7377fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
7378fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
7379dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
7380dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
73817a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
7382dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
7383108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
73843711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        final Uri rawContactsUri;
73853711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        if (mapsToProfileDb(uri)) {
738682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Pre-authorize the URI, since the caller would have already gone through the
738782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // permission check to get here, but the pre-authorization at the top level wouldn't
738882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // carry over to the raw contact.
738982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            rawContactsUri = preAuthorizeUri(RawContactsEntity.PROFILE_CONTENT_URI);
73903711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        } else {
73913711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            rawContactsUri = RawContactsEntity.CONTENT_URI;
73923711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        }
7393108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
7394108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
73953711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null, rawContactsUri)) {
7396108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
7397108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
7398108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7399d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7400108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
7401108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
7402108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7403108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
7404108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
7405108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
7406108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
7407108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
7408108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
7409108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
7410108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
7411108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
7412108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
7413d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7414d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7415d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7416b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
74174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
74184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
7419415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7420415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
7421415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7422a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
74234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
7424b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
7425be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
74262d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
7427b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
7428b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
742924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
7430b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
7431f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
743242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
743324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
7434f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
7435f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
7436bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
7437bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
7438f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO:
7439f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
7440f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
7441f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO:
7442f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO:
7443f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return "image/jpeg";
7444b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
744524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
7446be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
7447b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
744824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
7449b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
7450f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
745124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
7452f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
7453508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
74545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = ContentUris.parseId(uri);
74555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
74565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mProfileHelper.getDataMimeType(id);
74575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                } else {
74585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mContactsHelper.getDataMimeType(id);
74595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
746048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
746148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
746248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
746348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
74649005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
74659005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
746648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
746748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
746848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
746948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
747048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
747148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
747248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
747348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
7474b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
7475b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
7476b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
7477b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
7478b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
7479b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
7480b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
7481b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
7482c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
7483c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
7484c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
7485c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
7486d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
7487d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
7488d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
7489d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
7490af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS:
7491af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_TYPE;
7492af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID:
7493af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_ITEM_TYPE;
7494af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS:
7495af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_TYPE;
7496af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS_ID:
7497af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_ITEM_TYPE;
7498af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_PHOTOS:
7499af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                throw new UnsupportedOperationException("Not supported for write-only URI " + uri);
750061efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
750161efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
75024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
75034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
75047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
750509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
750609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
750709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
750809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
750909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
751009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
751109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
751209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
751324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
751409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
751509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
75168727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
751724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
75188727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
75198727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
752009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
752109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
752224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
752309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
752409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
752509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
752609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
752724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
752824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
752909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
753009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
753109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
753209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
753309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
753409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
753509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
753609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
753709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
753824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
753909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
754009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
754109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
754209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
754309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
754409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
754509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
754609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
754709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
754809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
754909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
755009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
755109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
755209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
755309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
755409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
755509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
755609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
755709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
755809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
755909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
7560f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
7561f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7562f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
7563f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
7564f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7565f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7566f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7567f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
7568f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
75695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().insertNameLookup(rawContactId, dataId, lookupType, name);
7570f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7571f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7572f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7573f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
7574d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
7575f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7576f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
7577f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
75782d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
7579d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
7580d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
7581d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
7582d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
7583d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
7584d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
7585d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
7586e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
7587916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
7588916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
7589e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
7590e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
75919a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
75929a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
75939a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
75949a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
75959a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
75969a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
75979a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
75989a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
75999a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
76009a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
76019a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
76029a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
76039a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
76049a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
76059a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
76064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
76077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
76087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
76097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
76107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
76117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
76127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
76137a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
76147a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
76157a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
7617f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
7618f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
76197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
76217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
76227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
76237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
76247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
76257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
76267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
76277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
76287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
76297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
76307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
76317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
76327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
76347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
76367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
76377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
76387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
76397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
76407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
76417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
76427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
76437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
76457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
76467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
76477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
76487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
76497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
76507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
76517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
76527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
76547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
76554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
76564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
76574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
7658b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
7659b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
7660b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
7661b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
7662b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
76634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
76644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
7665b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
7666b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
7667b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
7668caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
76695e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
76705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
76715e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
76725e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
76735e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
76745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
76755e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
76765e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
76775e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
76785e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
76795e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
7680caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
7681caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
7682caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
76835f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
7684caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
7685caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
7686caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
7687caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
76886f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
7689caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
76906f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
7691caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
7692f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
769373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
769443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Returns true if the specified account type and data set is writable.
769573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
769643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    protected boolean isWritableAccountWithDataSet(String accountTypeAndDataSet) {
769743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (accountTypeAndDataSet == null) {
7698bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
7699bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
7700bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
770143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Boolean writable = mAccountWritability.get(accountTypeAndDataSet);
770273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
770373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
770473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
770573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
7706627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
7707627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
770843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // TODO(dsantoro): Need to update this logic to allow for sub-accounts.
7709627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
7710627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
771143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountTypeAndDataSet.equals(sync.accountType)) {
771273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
771373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
7714627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
7715627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
7716627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
7717627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
7718627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
771973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
772073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
772173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
772273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
772373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
772443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        mAccountWritability.put(accountTypeAndDataSet, writable);
772573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
7726627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
7727b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
7728d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
7729f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
7730f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
7731f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7732f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
7733f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7734f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7735f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7736f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7737f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7738f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
7739f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
7740f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7741f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7742f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7743f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
7744f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7745f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
7746f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
7747f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7748f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7749f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
7750f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
7751f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
7752f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
7753f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
7754f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7755f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7756f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
7757f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
7758f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
7759f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
7760f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7761f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7762f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
7763f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7764f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7765f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
7766f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
7767f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7768f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
7769f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
7770f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
7771f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
7772f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
7773f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
77745fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
77755fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
77765fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
77775fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
77785fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
77795fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
77805fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
77815fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
77825fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
77835fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
77845fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
7785f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7786f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7787f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
7788f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7789f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
7790f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
7791f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7792f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7793f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
7794f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
7795f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
7796f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7797f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7798f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7799f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
7800f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
7801f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
7802f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
7803f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
7804f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7805f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7806f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
7807f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
78085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
78090dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
78100dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
78110dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
78120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
78130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
78145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(mContactsHelper.getProperty(
78155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROPERTY_AGGREGATION_ALGORITHM, "1"));
78160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
78170dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
78180dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
7819bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
78200dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
78210dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
78220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
78230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
78240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
78250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
78265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = null;
78270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
78285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
78295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db = mContactsHelper.getWritableDatabase();
78305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(db);
78315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.beginTransaction();
78325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = db.query(true,
78330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
78340dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
78350dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
78360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
78370dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
783843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE +
783943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.DATA_SET + "=r2." + RawContacts.DATA_SET,
78400dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
78410dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
78420dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
78430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
78440dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
78450dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
78460dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
78470dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
78480dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
78490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
78500dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
78515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.aggregateInTransaction(mTransactionContext.get(), db);
7852bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
78535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
78545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactsHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
78550dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
78560dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
78575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (db != null) {
78585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.endTransaction();
78595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
78600dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
78610dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
78620dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
78630dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
78640dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
78659a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
78669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
78679a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
78689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
78699a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
78709a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
78719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
78729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
78739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
787446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
787546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
787646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
787746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
787846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
787946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
788046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
788146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
788246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
788346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
788446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
788546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
788646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
788746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
788846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
788946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
789046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
789146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
789246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
789346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
789446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
789546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
78965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final Cursor cursor = mActiveDb.get().query(
7897ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                Views.DATA,
789846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
789946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
790046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
790146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
790246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
790346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
790446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
79055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().update(Tables.CONTACTS, values2, Contacts._ID + "=?",
79065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mSelectionArgs1);
79075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
79085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
790946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
791046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
791146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
791246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
791346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
791446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
791546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
791646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
791746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
791846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
791946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
792046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
792146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
7922f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    @VisibleForTesting
7923f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    /* package */ int updateDataUsageStat(
7924f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            List<Long> dataIds, String type, long currentTimeMillis) {
792546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
792646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
792746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
792846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
792946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
793046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
793146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
793246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
79335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().beginTransaction();
793446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
79355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final Cursor cursor = mActiveDb.get().query(Tables.DATA_USAGE_STAT, columns, where,
79365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        args, null, null, null);
793746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
793846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
793946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
794046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
794146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
794246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
794346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
794446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
794546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
79465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            mActiveDb.get().update(Tables.DATA_USAGE_STAT, values,
794746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
794846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
794946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
795046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
795146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
795246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
795346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
795446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
795546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
79565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get().insert(Tables.DATA_USAGE_STAT, null, values);
795746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
79585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mActiveDb.get().setTransactionSuccessful();
795946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
796046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
796146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
796246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
79635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().endTransaction();
796446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
796546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
796646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
796746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
796846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
796946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
797046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
797146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
797246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
797346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
797446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
797546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
797646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
797746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
797846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
797946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
798046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
798146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
798246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
798346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
798446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
798546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
798646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
798746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
798846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
798946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
799046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
799146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
799246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
799346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
799446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
799546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
799646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
799746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
7998b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7999b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
8000b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the URI for a deferred snippeting request
8001b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a deferred snippeting request is in the RI
8002b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
8003b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean deferredSnippetingRequested(Uri uri) {
8004b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        String deferredSnippeting =
8005b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            getQueryParameter(uri, SearchSnippetColumns.DEFERRED_SNIPPETING_KEY);
8006b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return !TextUtils.isEmpty(deferredSnippeting) &&  deferredSnippeting.equals("1");
8007b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
8008b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
8009b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
8010b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks if query is a single word or not.
8011b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if the query is one word or not
8012b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
8013b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean isSingleWordQuery(String query) {
8014b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return query.split(QUERY_TOKENIZER_REGEX).length == 1;
8015b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
8016b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
8017b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
8018b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the projection for a SNIPPET column indicating that a snippet is needed
8019b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a snippet is needed or not.
8020b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
8021b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean snippetNeeded(String [] projection) {
8022b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return mDbHelper.get().isInProjection(projection, SearchSnippetColumns.SNIPPET);
8023b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
80244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
8025