ContactsProvider2.java revision d0eb93009559d095de0448907527aeb059801dc4
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;
302f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawaimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
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;
452f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawaimport com.android.providers.contacts.util.DbQueryUtils;
4697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardComposer;
4797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardConfig;
4897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
4997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
5097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
51f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawaimport com.google.common.annotations.VisibleForTesting;
5297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
53b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
54caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
555b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
56bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.Notification;
57bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.NotificationManager;
58bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.PendingIntent;
59c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
60568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
61568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
626ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
6335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
6467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
6567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
66627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
67bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.Intent;
68568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
70627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
7167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
720bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.pm.PackageManager;
730bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.pm.PackageManager.NameNotFoundException;
745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoroimport android.content.pm.ProviderInfo;
75f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringertimport android.content.res.AssetFileDescriptor;
763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.content.res.Resources;
770bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmannimport android.content.res.Resources.NotFoundException;
78409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onukiimport android.database.AbstractCursor;
79e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CrossProcessCursor;
804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
81e2adda196b19047bc5243d2bffe9e5650e17e39dDmitri Plotnikovimport android.database.CursorWindow;
82ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
83ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
8409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
8509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
8708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
89f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.Bitmap;
90f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.BitmapFactory;
914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
92d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
93c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.AsyncTask;
94bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Binder;
956ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
96bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Handler;
97bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.HandlerThread;
98bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Message;
99ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
100c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoroimport android.os.ParcelFileDescriptor.AutoCloseInputStream;
101bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovimport android.os.Process;
102b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
10315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikovimport android.os.StrictMode;
1040dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
1050e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
1063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
107508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
1083de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
109b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
11082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoroimport android.provider.ContactsContract.Authorization;
11197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
11297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
11397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
11497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
1156d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Note;
11697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
11797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
11897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
1194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
12097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
12197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
122ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
1233de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
1245b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
1253de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
12671340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport android.provider.ContactsContract.DataUsageFeedback;
127d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
128f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.DisplayPhoto;
1293de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
130bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1313de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
1321dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoroimport android.provider.ContactsContract.PhotoFiles;
1330c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoroimport android.provider.ContactsContract.Profile;
13409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1353de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
1363711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenenimport android.provider.ContactsContract.RawContactsEntity;
137916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1383de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
13982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
1403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmannimport android.provider.ContactsContract.StreamItemPhotos;
141f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.StreamItems;
14297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
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
238d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
2392f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            + TIMES_USED_SORT_COLUMN + " DESC, "
2409b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
241d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
242d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
243d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
244d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
24545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa    private static final String FREQUENT_ORDER_BY = DataUsageStatColumns.TIMES_USED + " DESC,"
24645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
24745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
2486e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
2499b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
2509b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2519b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
2529b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
2536e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
2549b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
2559b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
2569b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
2579b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
258de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
259de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
2603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // Regex for splitting query strings - we split on any group of non-alphanumeric characters,
2613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    // excluding the @ symbol.
2623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    /* package */ static final String QUERY_TOKENIZER_REGEX = "[^\\w@]+";
2633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
264d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
265d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
2665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
2675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
268a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
2695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
2705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
2715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
2725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
273a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
274bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_PHOTO = 1010;
275bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_PHOTO = 1011;
276bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_DISPLAY_PHOTO = 1012;
277bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DISPLAY_PHOTO = 1013;
278bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DISPLAY_PHOTO = 1014;
279bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_VCARD = 1015;
280bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_AS_MULTI_VCARD = 1016;
281bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_DATA = 1017;
282bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_DATA = 1018;
283bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_ENTITIES = 1019;
284bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ENTITIES = 1020;
285bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1021;
286bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_ID_STREAM_ITEMS = 1022;
287bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_STREAM_ITEMS = 1023;
288bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_LOOKUP_ID_STREAM_ITEMS = 1024;
289bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro    private static final int CONTACTS_FREQUENT = 1025;
2904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2915ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2925ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2935ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
29446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
295f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_DISPLAY_PHOTO = 2006;
296f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS = 2007;
29782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro    private static final int RAW_CONTACTS_ID_STREAM_ITEMS_ID = 2008;
2984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
3006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
301ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
30248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
30348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
30448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
30548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
30648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
30748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
30848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
30948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
310a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
3126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
313b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
314b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
315b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
31682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
31782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
3181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
31931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
32031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
321eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
322eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
323ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
324ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
325ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
326ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
32735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
328b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
3295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE = 11002;
3305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_SYNCSTATE_ID = 11003;
33135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
332c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
333c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
334c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3351b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
3361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
3371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
3381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
3391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
34046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
34146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
34209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
34309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
344d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
345d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
346d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
3477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
3487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
34924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE = 19000;
35024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_ENTITIES = 19001;
35124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA = 19002;
35224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_DATA_ID = 19003;
35324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_AS_VCARD = 19004;
35424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS = 19005;
35524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID = 19006;
35624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
35724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
3585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final int PROFILE_STATUS_UPDATES = 19009;
3593202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro    private static final int PROFILE_RAW_CONTACT_ENTITIES = 19010;
36085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro    private static final int PROFILE_PHOTO = 19011;
36185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro    private static final int PROFILE_DISPLAY_PHOTO = 19012;
36224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
36346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final int DATA_USAGE_FEEDBACK_ID = 20001;
36446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
3653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS = 21000;
3663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_PHOTOS = 21001;
3673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID = 21002;
3683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS = 21003;
3693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_ID_PHOTOS_ID = 21004;
3703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final int STREAM_ITEMS_LIMIT = 21005;
3713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
372f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int DISPLAY_PHOTO = 22000;
373f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private static final int PHOTO_DIMENSIONS = 22001;
374f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
3755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Inserts into URIs in this map will direct to the profile database if the parent record's
3765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // value (looked up from the ContentValues object with the key specified by the value in this
3775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // map) is in the profile ID-space (see {@link ProfileDatabaseHelper#PROFILE_ID_SPACE}).
3785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private static final Map<Integer, String> INSERT_URI_ID_VALUE_MAP = Maps.newHashMap();
3795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    static {
3805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(DATA, Data.RAW_CONTACT_ID);
3815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_DATA, Data.RAW_CONTACT_ID);
3825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STATUS_UPDATES, StatusUpdates.DATA_ID);
3835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(RAW_CONTACTS_ID_STREAM_ITEMS, StreamItems.RAW_CONTACT_ID);
3855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        INSERT_URI_ID_VALUE_MAP.put(STREAM_ITEMS_ID_PHOTOS, StreamItemPhotos.STREAM_ITEM_ID);
3875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
3885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
38936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // Any interactions that involve these URIs will also require the calling package to have either
39036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // android.permission.READ_SOCIAL_STREAM permission or android.permission.WRITE_SOCIAL_STREAM
39136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    // permission, depending on the type of operation being performed.
39236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private static final List<Integer> SOCIAL_STREAM_URIS = Lists.newArrayList(
39336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_ID_STREAM_ITEMS,
39436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_LOOKUP_STREAM_ITEMS,
39536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            CONTACTS_LOOKUP_ID_STREAM_ITEMS,
39636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            RAW_CONTACTS_ID_STREAM_ITEMS,
39736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            RAW_CONTACTS_ID_STREAM_ITEMS_ID,
39836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS,
39936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_PHOTOS,
40036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID,
40136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID_PHOTOS,
40236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            STREAM_ITEMS_ID_PHOTOS_ID
40336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    );
40436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
405dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_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)"
415dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
416dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
417dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
418dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
419dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
420dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
421dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
42243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND ("
42343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + "="
42443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " OR "
42543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
42643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)"
42743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + " AND " + Groups.AUTO_ADD + " != 0";
428dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
429dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
430dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
431dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
432dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
433dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
434dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
435dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
436dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
437dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
438dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
439dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
440d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
441f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
442f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
443f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
44467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
44567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
4466cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
4476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_TYPE,
4486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            RawContactsColumns.CONCRETE_ACCOUNT_NAME,
44943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContactsColumns.CONCRETE_DATA_SET,
4503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
451f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
452ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
453ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
454d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
4556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_TYPE = 1;
4566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        public static final int ACCOUNT_NAME = 2;
45743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_SET = 3;
45843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int DATA_ID = 4;
45943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        public static final int CONTACT_ID = 5;
460ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
4611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
462f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov    interface RawContactsQuery {
46319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
46419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
46519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
466ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
467ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
468ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
46943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.DATA_SET,
47019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
47119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
47219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
473ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
474ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
47543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int DATA_SET = 3;
47619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
47719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
478c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
479caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
48071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
48171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
48271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
48371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
48471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
48571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
48671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
48771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
4887cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
4897cf50494501938f175d288077145acf49da8f171Daniel Lehmann                                    + " AND " + GroupMembership.GROUP_ROW_ID + "="
49071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
49171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
49271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
49371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
494a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
495a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
496a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
497a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
498a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
499a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
500a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
501a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
502a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
503a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
504a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
505a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
506c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
507c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
508c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
509c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
510c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
511c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
512f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    private static final String TIME_SINCE_LAST_USED =
513f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            "(strftime('%s', 'now') - " + DataUsageStatColumns.LAST_TIME_USED + "/1000)";
514f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa
515c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
516c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
5172262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * second in_visible_group, then the rest.
5182262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * Within the four (starred/unstarred, in_visible_group/not-in_visible_group) groups
5192262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa     * - three buckets: very recently contacted, then fairly
520c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
52146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
52246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * (Super)primary email address is returned before other addresses for the same contact.
523c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
524c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
5252262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        Contacts.STARRED + " DESC, "
5262262f0ccc800b93a9d1e63c55654ca3aaf5e7d1cDaisuke Miyakawa        + Contacts.IN_VISIBLE_GROUP + " DESC, "
527f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa        + "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
52846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 0 "
529f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa                + " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
53046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " THEN 1 "
53146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + " ELSE 2 END), "
53246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + DataUsageStatColumns.TIMES_USED + " DESC, "
53346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Contacts.DISPLAY_NAME + ", "
53446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        + Data.CONTACT_ID + ", "
535c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_SUPER_PRIMARY + " DESC, "
536c591cc2ffecdd0038f787a133606752752294c13Daisuke Miyakawa        + Data.IS_PRIMARY + " DESC";
53746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
53846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
53946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
540c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
541916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
542916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
543916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
544916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
54592ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            NameLookupType.NICKNAME;
546916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
547f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
548f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
549f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
550f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
551f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
552f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
553f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
554f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
555f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
556f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
55743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.DATA_SET,
55843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
559f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
560f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
561f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
562f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
563f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
564916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
576f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            .add(Contacts.PHOTO_FILE_ID)
5773d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
5783d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
584cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
60503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET)
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
61143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.DATA_SET)
61243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(RawContacts.ACCOUNT_TYPE_AND_DATA_SET)
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
681038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
686e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
69124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
696916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
698f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
701916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
7025e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
704f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
7052f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
709f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
7102f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + ")")
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
712f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. Right now Starred part just returns NULL for
7164928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * those data columns (frequent part should return real ones in data table).
7174928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyStarredProjectionMap
7194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
7224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER, "NULL")
7234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE, "NULL")
7244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL, "NULL")
7254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7264928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
7274928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    /**
7284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Used for Strequent Uri with {@link ContactsContract#STREQUENT_PHONE_ONLY}, which allows
7294928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * users to obtain part of Data columns. We hard-code {@link Contacts#IS_USER_PROFILE} to NULL,
7304928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * because sContactsProjectionMap specifies a field that doesn't exist in the view behind the
7314928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * query that uses this projection map.
7324928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     **/
7334928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa    private static final ProjectionMap sStrequentPhoneOnlyFrequentProjectionMap
7344928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            = ProjectionMap.builder()
7354928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .addAll(sContactsProjectionMap)
7364928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(TIMES_USED_SORT_COLUMN, DataUsageStatColumns.CONCRETE_TIMES_USED)
7374928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.NUMBER)
7384928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.TYPE)
7394928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Phone.LABEL)
7404928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .add(Contacts.IS_USER_PROFILE, "NULL")
7414928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            .build();
7424928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
743f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
745fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            .add(Contacts._ID)
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
750ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
76824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
773a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
78024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
786a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
790f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
79424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(Contacts.IS_USER_PROFILE)
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
799f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
8034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
80924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
810f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
812f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
813f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
814f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
815f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
816f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
8175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
818f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
819f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
820f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
82124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            .add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
822f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
823f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
824f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
825f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
826f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
827f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
8289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
829f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
830f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
831f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
832f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
833f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
834f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
835f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
836f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
837f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
8383d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
8393d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
840f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
841f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
842f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
843f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
844f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
845f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
8462530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
847f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
848f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
849ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
850f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
851f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
852f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
853f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
85443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.DATA_SET)
85543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            .add(Groups.ACCOUNT_TYPE_AND_DATA_SET)
856f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
857f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
858f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
859f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
860f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
861f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
862f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
863f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
864f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
865f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
866f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
867f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
868f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
869c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
870f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
871f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
872f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
873f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
874f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
875f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
876ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
877f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
878f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
879f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
880f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
881f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
882f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")")
883f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
884f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(" + ContactsColumns.CONCRETE_ID + ") FROM "
885f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + Tables.CONTACTS_JOIN_RAW_CONTACTS_DATA_FILTERED_BY_GROUPMEMBERSHIP
886f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " WHERE " + Contacts.HAS_PHONE_NUMBER + ")")
887f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .build();
888f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa
889f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // This is only exposed as hidden API for the contacts app, so we can be very specific in
890f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    // the filtering
891f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa    private static final ProjectionMap sGroupsSummaryProjectionMapWithGroupCountPerAccount =
892f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            ProjectionMap.builder()
893f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .addAll(sGroupsSummaryProjectionMap)
894f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa            .add(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
895f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                    "(SELECT COUNT(*) FROM " + Views.GROUPS + " WHERE "
896f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + "(" + Groups.ACCOUNT_NAME + "="
897f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + GroupsColumns.CONCRETE_ACCOUNT_NAME
898f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
899f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_TYPE + "=" + GroupsColumns.CONCRETE_ACCOUNT_TYPE
900f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + " AND "
901f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.DELETED + "=0 AND "
902f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.FAVORITES + "=0 AND "
903f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.AUTO_ADD + "=0"
904f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + ")"
905f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        + " GROUP BY "
906f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                            + Groups.ACCOUNT_NAME + ", " + Groups.ACCOUNT_TYPE
907f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                   + ")")
908f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
909f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
910373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
911f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
912f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
913f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
914f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
915f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
916f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
917f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
918eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
919f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
920f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
921f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
922f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            .add(Settings.DATA_SET)
923f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
924f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
925f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
926f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
927f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
928f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
929f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
930f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
931f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
932f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
933f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
934f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
935f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
936f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE
937f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                + " AND ((" + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND "
938f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + " IS NULL) OR ("
939f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + GroupsColumns.CONCRETE_DATA_SET + "="
940f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                                    + SettingsColumns.CONCRETE_DATA_SET + "))))=0"
941f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
942f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
943f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
944f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
945f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
946f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
947f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
948f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
949f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
950f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
951f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
952f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
953f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
954f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
955f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
956f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
957f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
958f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
959f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
960f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
96182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
962f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
963f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
964f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
965f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
966f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
967f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
968f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
969f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
970f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
971f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
972f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
973f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
974f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
975f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
976f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
977f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
978f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
979f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
980f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
981f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
982f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
9833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /** Contains StreamItems columns */
9843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemsProjectionMap = ProjectionMap.builder()
9859b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems._ID)
9869b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.CONTACT_ID)
987af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann            .add(StreamItems.CONTACT_LOOKUP_KEY)
9889b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_NAME)
9899b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.ACCOUNT_TYPE)
9909b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.DATA_SET)
9913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
9929b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID)
9933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_PACKAGE)
9943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_ICON)
9953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RES_LABEL)
9963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TEXT)
9973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.TIMESTAMP)
9983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.COMMENTS)
9990bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC1)
10000bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC2)
10010bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC3)
10020bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.SYNC4)
10033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
10043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
10053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private static final ProjectionMap sStreamItemPhotosProjectionMap = ProjectionMap.builder()
10063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos._ID, StreamItemPhotosColumns.CONCRETE_ID)
10073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItems.RAW_CONTACT_ID)
10080bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItems.RAW_CONTACT_SOURCE_ID, RawContactsColumns.CONCRETE_SOURCE_ID)
10093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.STREAM_ITEM_ID)
10103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .add(StreamItemPhotos.SORT_INDEX)
10116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_FILE_ID)
10126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            .add(StreamItemPhotos.PHOTO_URI,
10136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    "'" + DisplayPhoto.CONTENT_URI + "'||'/'||" + StreamItemPhotos.PHOTO_FILE_ID)
10141dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.HEIGHT)
10151dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.WIDTH)
10161dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro            .add(PhotoFiles.FILESIZE)
10170bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC1)
10180bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC2)
10190bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC3)
10200bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            .add(StreamItemPhotos.SYNC4)
10213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            .build();
10223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
10231b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
1024f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
1025f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
1026f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
1027f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
1028f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
1029f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
1030f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
1031f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
1032d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
1033f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
1034f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
1035f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
1036f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
1037f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
1038f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
1039f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
1040f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
1041f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
1042778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
1043778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
1044f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
10457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
10479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
10489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
10499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
10509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
10519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
10522526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
10532526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1054bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1055bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
1056bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1057bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
105851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
105903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_START_MATCH = "[";
106003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_END_MATCH = "]";
106103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final String DEFAULT_SNIPPET_ARG_ELLIPSIS = "...";
106203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private static final int DEFAULT_SNIPPET_ARG_MAX_TOKENS = -10;
106303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
10649a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhoneInitialized;
10659a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    private boolean sIsPhone;
10669a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
1067f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
10681129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
10691129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
10702526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
10712526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
1072f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
1073f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
107446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
107546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
107646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type integers in {@link DataUsageStatColumns}.
107746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
107846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private static final Map<String, Integer> sDataUsageTypeMap;
107946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
10804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
10814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
1082a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
1083d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
1084d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
1085a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
1086a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
10873653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
10883653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
10892d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
10902d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
1091a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
1092f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
1093f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_ID_DISPLAY_PHOTO);
10943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
10953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_ID_STREAM_ITEMS);
1096c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
10975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
10985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
10992149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
1100bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/photo",
1101bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_PHOTO);
11025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
11032149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
11042149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
1105bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/photo",
1106bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                CONTACTS_LOOKUP_ID_PHOTO);
1107f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
1108f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_DISPLAY_PHOTO);
1109f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
1110f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
1111a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
1112a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
1113a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
1114a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
11153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
11163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_STREAM_ITEMS);
11173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
11183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                CONTACTS_LOOKUP_ID_STREAM_ITEMS);
1119f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
112042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
112142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
11225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
1123ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
1124ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
11255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
112645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
11273653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
11285ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
11295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
11305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
1131f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
1132f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                RAW_CONTACTS_ID_DISPLAY_PHOTO);
113346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
11343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
11353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                RAW_CONTACTS_ID_STREAM_ITEMS);
113682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items/#",
113782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                RAW_CONTACTS_ID_STREAM_ITEMS_ID);
113846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
113946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
1140b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
11414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
11424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
1143ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
114448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
11455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
1146ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
11474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
114848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
11491dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
11505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
11515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
11524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
1153ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
115448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
115546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        /** "*" is in CSV form with data ids ("123,456,789") */
115646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
11571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1158ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
1159ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
1160ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
1161ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
116235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
1163b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
1164b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
11655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/" + SyncStateContentProviderHelper.PATH,
11665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE);
11675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY,
11685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                "profile/" + SyncStateContentProviderHelper.PATH + "/#",
11695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_SYNCSTATE_ID);
117035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1171a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
1172b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
1173b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
1174b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
1175b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
11764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1177eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
1178eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
117982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
118082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
11811f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1182c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
1183c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
1184c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
1185c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
11862d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
1187c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
1188c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
11891b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
11901b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
11911b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
11921b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
11931b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
11941b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
11951b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
11961b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
119709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
119809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
1199d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1200d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
1201d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
12027a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
12037a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
120424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
120524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
120624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
120724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
120824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
120985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/photo", PROFILE_PHOTO);
121085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/display_photo", PROFILE_DISPLAY_PHOTO);
121124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
121224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
121324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
121424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID);
121524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
121624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_DATA);
121724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
121824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                PROFILE_RAW_CONTACTS_ID_ENTITIES);
12195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/status_updates",
12205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROFILE_STATUS_UPDATES);
12213202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contact_entities",
12223202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro                PROFILE_RAW_CONTACT_ENTITIES);
122346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
12243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
12253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
12263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
12273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
12283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
12293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                STREAM_ITEMS_ID_PHOTOS_ID);
12303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
12313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
12325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", DISPLAY_PHOTO);
1233f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
1234f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
123546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
123646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
123746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
123846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
123946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
124046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
124146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
124219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
124319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
1244d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
1245d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
1246d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
1247d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
1248d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
1249d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
1250d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
1251d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
1252d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
12534458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
12544458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
1255d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
12563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
125743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * An entry in group id cache. It maps the combination of (account type, account name, data set,
1258ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1259ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1260e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
1261ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1262ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
126343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet;
1264ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1265ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1266ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1267a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1268e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1269e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1270e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
1271e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1272e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
127324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    /**
1274f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of display photos.  Larger images will be scaled
1275f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to fit.
1276f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1277f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxDisplayPhotoDim;
1278f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1279f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
1280f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Maximum dimension (height or width) of photo thumbnails.
1281f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
1282f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private int mMaxThumbnailPhotoDim;
1283f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
12855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Sub-provider for handling profile requests against the profile database.
12865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
12875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileProvider mProfileProvider;
1288f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
12894097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1290f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1291315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1292622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1293622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
129472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
12955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1296078f588cef389358adabc579de00747878f3c108Dave Santoro    // The database tag to use for representing the contacts DB in contacts transactions.
1297078f588cef389358adabc579de00747878f3c108Dave Santoro    /* package */ static final String CONTACTS_DB_TAG = "contacts";
1298078f588cef389358adabc579de00747878f3c108Dave Santoro
1299078f588cef389358adabc579de00747878f3c108Dave Santoro    // The database tag to use for representing the profile DB in contacts transactions.
1300078f588cef389358adabc579de00747878f3c108Dave Santoro    /* package */ static final String PROFILE_DB_TAG = "profile";
1301078f588cef389358adabc579de00747878f3c108Dave Santoro
13025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
13035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * The active (thread-local) database.  This will be switched between a contacts-specific
13045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database and a profile-specific database, depending on what the current operation is
13055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * targeted to.
13065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
13075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<SQLiteDatabase> mActiveDb = new ThreadLocal<SQLiteDatabase>();
13085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13096efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    /**
13106efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * The thread-local holder of the active transaction.  Shared between this and the profile
13116efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     * provider, to keep transactions on both databases synchronized.
13126efb7db26598b105342d02207e0ca1c8725c10daDave Santoro     */
13136efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    private final ThreadLocal<ContactsTransaction> mTransactionHolder =
13146efb7db26598b105342d02207e0ca1c8725c10daDave Santoro            new ThreadLocal<ContactsTransaction>();
13156efb7db26598b105342d02207e0ca1c8725c10daDave Santoro
13165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // This variable keeps track of whether the current operation is intended for the profile DB.
13175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<Boolean> mInProfileMode = new ThreadLocal<Boolean>();
13185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Separate data row handler instances for contact data and profile data.
13205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mDataRowHandlers;
13215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private HashMap<String, DataRowHandler> mProfileDataRowHandlers;
13225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile, we will use one of two
13245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // database helper instances.
13255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactsDatabaseHelper> mDbHelper =
13265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<ContactsDatabaseHelper>();
13275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactsDatabaseHelper mContactsHelper;
13285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ProfileDatabaseHelper mProfileHelper;
13295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
13315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two aggregator instances.
13325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<ContactAggregator> mAggregator = new ThreadLocal<ContactAggregator>();
1333622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
13345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private ContactAggregator mProfileAggregator;
13355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Depending on whether the action being performed is for the profile or not, we will use one of
13375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // two photo store instances (with their files stored in separate subdirectories).
13385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<PhotoStore> mPhotoStore = new ThreadLocal<PhotoStore>();
13395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mContactsPhotoStore;
13405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private PhotoStore mProfilePhotoStore;
13415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
13425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // The active transaction context will switch depending on the operation being performed.
13435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // Both transaction contexts will be cleared out when a batch transaction is started, and
13445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    // each will be processed separately when a batch transaction completes.
13455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private TransactionContext mContactTransactionContext = new TransactionContext(false);
13465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private TransactionContext mProfileTransactionContext = new TransactionContext(true);
13475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private final ThreadLocal<TransactionContext> mTransactionContext =
13485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            new ThreadLocal<TransactionContext>();
13495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
135082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    // Duration in milliseconds that pre-authorized URIs will remain valid.
135182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private long mPreAuthorizedUriDuration;
135282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
135382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    // Map of single-use pre-authorized URIs to expiration times.
135482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private Map<Uri, Long> mPreAuthorizedUris = Maps.newHashMap();
135582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
1356d0eb93009559d095de0448907527aeb059801dc4Dave Santoro    // Random number generator.
1357d0eb93009559d095de0448907527aeb059801dc4Dave Santoro    private SecureRandom mRandom = new SecureRandom();
135882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
1359f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1360a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1361d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1362f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    private SearchIndexManager mSearchIndexManager;
1363a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
136420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
136573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
136620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
136709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
13683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
136909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
137015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mReadAccessLatch;
137115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private volatile CountDownLatch mWriteAccessLatch;
137215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private boolean mAccountUpdateListenerRegistered;
1373bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private boolean mOkToOpenAccess = true;
137473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
13751a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
13761a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
137781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
137881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
13794cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
13803826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
1381d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1382bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private HandlerThread mBackgroundThread;
1383bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private Handler mBackgroundHandler;
1384bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1385f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mLastPhotoCleanup = 0;
1386f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
13874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
13884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1389663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1390663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate start");
1391663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        }
1392de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1393ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1394ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1395ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1396ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1397ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1398663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki        } finally {
1399663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
1400663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki                Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate finish");
1401663b8b8ce7a29fb2796dc6431f2cd5992934f315Makoto Onuki            }
1402ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1403ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
140435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1405ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
140615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        StrictMode.setThreadPolicy(
140715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
140815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
14093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Resources resources = getContext().getResources();
1410f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxDisplayPhotoDim = resources.getInteger(
1411f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_display_photo_dim);
1412f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mMaxThumbnailPhotoDim = resources.getInteger(
1413f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                R.integer.config_max_thumbnail_photo_dim);
14143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
1415078f588cef389358adabc579de00747878f3c108Dave Santoro        mContactsHelper = getDatabaseHelper(getContext());
14165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
1417078f588cef389358adabc579de00747878f3c108Dave Santoro
1418078f588cef389358adabc579de00747878f3c108Dave Santoro        // Set up the DB helper for keeping transactions serialized.
1419078f588cef389358adabc579de00747878f3c108Dave Santoro        setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG);
1420078f588cef389358adabc579de00747878f3c108Dave Santoro
142172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1422a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
142365ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1424bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // The provider is closed for business until fully initialized
142515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mReadAccessLatch = new CountDownLatch(1);
142615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        mWriteAccessLatch = new CountDownLatch(1);
142772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1428bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread = new HandlerThread("ContactsProviderWorker",
1429bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                Process.THREAD_PRIORITY_BACKGROUND);
1430bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundThread.start();
1431bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler = new Handler(mBackgroundThread.getLooper()) {
1432bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            @Override
1433bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            public void handleMessage(Message msg) {
1434bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                performBackgroundTask(msg.what, msg.obj);
1435bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1436bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        };
14372a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
14385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set up the sub-provider for handling profiles.
14395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider = getProfileProvider();
1440c990980ab4beb7b81c3337526f1bdcd5d1a14730Dave Santoro        mProfileProvider.setDbHelperToSerializeOn(mContactsHelper, CONTACTS_DB_TAG);
14415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        ProviderInfo profileInfo = new ProviderInfo();
14425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.readPermission = "android.permission.READ_PROFILE";
14435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileInfo.writePermission = "android.permission.WRITE_PROFILE";
14445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileProvider.attachInfo(getContext(), profileInfo);
1445078f588cef389358adabc579de00747878f3c108Dave Santoro        mProfileHelper = mProfileProvider.getDatabaseHelper(getContext());
14465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
144782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        // Initialize the pre-authorized URI duration.
144882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        mPreAuthorizedUriDuration = android.provider.Settings.Secure.getLong(
144982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                getContext().getContentResolver(),
145082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                android.provider.Settings.Secure.CONTACTS_PREAUTH_URI_EXPIRATION,
145182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                DEFAULT_PREAUTHORIZED_URI_EXPIRATION);
145282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
145315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_INITIALIZE);
1454bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
1455bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
1456bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_LOCALE);
1457bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM);
145805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_SEARCH_INDEX);
1459bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_PROVIDER_STATUS);
146015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_OPEN_WRITE_ACCESS);
1461f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
14623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
146349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
14644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
14654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1466767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
146751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
146851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
146904b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
147015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        Context context = getContext();
14715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mLegacyApiSupport = new LegacyApiSupport(context, mContactsHelper, this,
14725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mGlobalSearchSupport);
14734cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
14745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mNameSplitter = mContactsHelper.createNameSplitter();
14754cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
14764cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
14775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mCommonNicknameCache = new CommonNicknameCache(mContactsHelper.getReadableDatabase());
1478cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
14795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactAggregator = new ContactAggregator(this, mContactsHelper,
148015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14815b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
14825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator = new ProfileAggregator(this, mProfileHelper,
14835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                createPhotoPriorityResolver(context), mNameSplitter, mCommonNicknameCache);
14845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1485f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov        mSearchIndexManager = new SearchIndexManager(this);
14865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore = new PhotoStore(getContext().getFilesDir(), mContactsHelper);
14885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore = new PhotoStore(new File(getContext().getFilesDir(), "profile"),
14895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfileHelper);
14905b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
1491bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
14925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mDataRowHandlers, mContactsHelper, mContactAggregator,
14935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsPhotoStore);
14945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileDataRowHandlers = new HashMap<String, DataRowHandler>();
14955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        initDataRowHandlers(mProfileDataRowHandlers, mProfileHelper, mProfileAggregator,
14965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mProfilePhotoStore);
14975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
14985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Set initial thread-local state variables for the Contacts DB.
14995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
15005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
1501bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
15025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private void initDataRowHandlers(Map<String, DataRowHandler> handlerMap,
15035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            ContactsDatabaseHelper dbHelper, ContactAggregator contactAggregator,
15045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            PhotoStore photoStore) {
15055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Context context = getContext();
15065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Email.CONTENT_ITEM_TYPE,
15075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForEmail(context, dbHelper, contactAggregator));
15085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Im.CONTENT_ITEM_TYPE,
15095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForIm(context, dbHelper, contactAggregator));
15105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Organization.CONTENT_ITEM_TYPE,
15115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForOrganization(context, dbHelper, contactAggregator));
15125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Phone.CONTENT_ITEM_TYPE,
15135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoneNumber(context, dbHelper, contactAggregator));
15145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Nickname.CONTENT_ITEM_TYPE,
15155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNickname(context, dbHelper, contactAggregator));
15165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredName.CONTENT_ITEM_TYPE,
15175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredName(context, dbHelper, contactAggregator,
1518bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mNameSplitter, mNameLookupBuilder));
15195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(StructuredPostal.CONTENT_ITEM_TYPE,
15205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForStructuredPostal(context, dbHelper, contactAggregator,
1521bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mPostalSplitter));
15225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(GroupMembership.CONTENT_ITEM_TYPE,
15235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForGroupMembership(context, dbHelper, contactAggregator,
1524bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                        mGroupIdCache));
15255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Photo.CONTENT_ITEM_TYPE,
15265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForPhoto(context, dbHelper, contactAggregator, photoStore));
15275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        handlerMap.put(Note.CONTENT_ITEM_TYPE,
15285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new DataRowHandlerForNote(context, dbHelper, contactAggregator));
1529bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1530bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1531bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /**
1532bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Visible for testing.
1533bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     */
1534bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
1535bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        return new PhotoPriorityResolver(context);
1536bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1537bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1538bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task) {
1539bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendEmptyMessage(task);
1540bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1541bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1542bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void scheduleBackgroundTask(int task, Object arg) {
1543bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mBackgroundHandler.sendMessage(mBackgroundHandler.obtainMessage(task, arg));
1544bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
1545bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1546bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void performBackgroundTask(int task, Object arg) {
1547bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        switch (task) {
154815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_INITIALIZE: {
154915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                initForDefaultLocale();
155015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch.countDown();
155115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                mReadAccessLatch = null;
155215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                break;
155315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            }
155415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
155515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            case BACKGROUND_TASK_OPEN_WRITE_ACCESS: {
1556bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (mOkToOpenAccess) {
155715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch.countDown();
155815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mWriteAccessLatch = null;
1559bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1560bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1561bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1562bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1563bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS: {
1564bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isLegacyContactImportNeeded()) {
1565bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    importLegacyContactsInBackground();
1566bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1567bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1568bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1569bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1570bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_ACCOUNTS: {
157115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Context context = getContext();
157215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                if (!mAccountUpdateListenerRegistered) {
157315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    AccountManager.get(context).addOnAccountsUpdatedListener(this, null, false);
157415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                    mAccountUpdateListenerRegistered = true;
157515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                }
157615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
15775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                // Update the accounts for both the contacts and profile DBs.
157815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Account[] accounts = AccountManager.get(context).getAccounts();
15795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToContactMode();
1580bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                boolean accountsChanged = updateAccountsInBackground(accounts);
15815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                switchToProfileMode();
15825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                accountsChanged |= updateAccountsInBackground(accounts);
15835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1584bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateContactsAccountCount(accounts);
1585bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateDirectoriesInBackground(accountsChanged);
1586bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1587bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1588bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1589bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_LOCALE: {
1590bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateLocaleInBackground();
1591bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1592bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1593bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1594fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            case BACKGROUND_TASK_CHANGE_LOCALE: {
1595fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                changeLocaleInBackground();
1596fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov                break;
1597fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            }
1598fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1599bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPGRADE_AGGREGATION_ALGORITHM: {
1600bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (isAggregationUpgradeNeeded()) {
1601bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    upgradeAggregationAlgorithmInBackground();
1602bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1603bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1604bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1605bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
160605e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_SEARCH_INDEX: {
160705e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                updateSearchIndexInBackground();
160805e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov                break;
160905e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov            }
161005e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1611bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_PROVIDER_STATUS: {
1612bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                updateProviderStatus();
1613bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1614bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1615bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1616bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            case BACKGROUND_TASK_UPDATE_DIRECTORIES: {
1617bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                if (arg != null) {
1618bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    mContactDirectoryManager.onPackageChanged((String) arg);
1619bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                }
1620bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                break;
1621bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            }
1622f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1623f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case BACKGROUND_TASK_CLEANUP_PHOTOS: {
1624f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Check rate limit.
1625f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long now = System.currentTimeMillis();
1626f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (now - mLastPhotoCleanup > PHOTO_CLEANUP_RATE_LIMIT) {
1627f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    mLastPhotoCleanup = now;
16285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
16295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // Clean up photo stores for both contacts and profiles.
16305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToContactMode();
16315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    cleanupPhotoStore();
16325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    switchToProfileMode();
1633f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    cleanupPhotoStore();
1634f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    break;
1635f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
1636f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1637bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
16384cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
16394cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
164053fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
16413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
16423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
16434f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
16444f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
16454f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
1646fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_CHANGE_LOCALE);
16474cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
164851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
164951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
165051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
165151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
165251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
165351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
165451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
165551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
1656bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateLocaleInBackground() {
1657f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
1658f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
1659f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
1660f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
1661f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
1662f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
166351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
166451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
166551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
166651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
166751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
166851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
166951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
167051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
167151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
16725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, currentLocale);
16735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.setLocale(this, currentLocale);
1674bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
1675bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        setProviderStatus(providerStatus);
1676bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
167751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
1678fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    /**
1679fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     * Reinitializes the provider for a new locale.
1680fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov     */
1681fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    private void changeLocaleInBackground() {
1682fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Re-initializing the provider without stopping it.
1683fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // Locking the database will prevent inserts/updates/deletes from
1684fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // running at the same time, but queries may still be running
1685fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        // on other threads. Those queries may return inconsistent results.
16865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mContactsHelper.getWritableDatabase();
16875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase profileDb = mProfileHelper.getWritableDatabase();
1688fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        db.beginTransaction();
16895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        profileDb.beginTransaction();
1690fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        try {
1691fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            initForDefaultLocale();
1692fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.setTransactionSuccessful();
16935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.setTransactionSuccessful();
1694fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        } finally {
1695fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov            db.endTransaction();
16965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            profileDb.endTransaction();
1697fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        }
1698fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
1699fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov        updateLocaleInBackground();
1700fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov    }
1701fd2a6a5b7ecbbec6298182daee3b252896f82ea4Dmitri Plotnikov
170205e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    protected void updateSearchIndexInBackground() {
170305e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov        mSearchIndexManager.updateIndex();
170405e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov    }
170505e50fbf9809bf04eceec3d2a2753630dc4f9315Dmitri Plotnikov
1706bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void updateDirectoriesInBackground(boolean rescan) {
1707bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mContactDirectoryManager.scanAllPackages(rescan);
170851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
170951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
17103826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
17113826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
17123826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
17133826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
17143826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
17153826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
17163e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        // No accounts/no contacts status is true if there are no account and
17175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // there are no contacts or one profile contact
17183e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson        if (mContactsAccountCount == 0) {
17195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long contactsNum = DatabaseUtils.queryNumEntries(mContactsHelper.getReadableDatabase(),
17203e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                    Tables.CONTACTS, null);
17215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long profileNum = DatabaseUtils.queryNumEntries(mProfileHelper.getReadableDatabase(),
17225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Tables.CONTACTS, null);
17235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
17245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // TODO: Different status if there is a profile but no contacts?
17255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (contactsNum == 0 && profileNum <= 1) {
17263e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
17273e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            } else {
17283e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson                setProviderStatus(ProviderStatus.STATUS_NORMAL);
17293e6cd1fbafd09bf9b6ec35a19a55b48a271727dfIsaac Katzenelson            }
17303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
17313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
17323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
17333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
17343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
173531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1736f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    protected void cleanupPhotoStore() {
17375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
1738d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        mActiveDb.set(db);
17396802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
17406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Assemble the set of photo store file IDs that are in use, and send those to the photo
1741f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // store.  Any photos that aren't in that set will be deleted, and any photos that no
1742f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // longer exist in the photo store will be returned for us to clear out in the DB.
17437cf50494501938f175d288077145acf49da8f171Daniel Lehmann        long photoMimeTypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
17446802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Cursor c = db.query(Views.DATA, new String[]{Data._ID, Photo.PHOTO_FILE_ID},
17457cf50494501938f175d288077145acf49da8f171Daniel Lehmann                DataColumns.MIMETYPE_ID + "=" + photoMimeTypeId + " AND "
1746f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + Photo.PHOTO_FILE_ID + " IS NOT NULL", null, null, null, null);
17476802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Set<Long> usedPhotoFileIds = Sets.newHashSet();
17486802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToDataId = Maps.newHashMap();
1749f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
1750f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            while (c.moveToNext()) {
17516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long dataId = c.getLong(0);
17526802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(1);
17536802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17546802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToDataId.put(photoFileId, dataId);
17556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
17566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } finally {
17576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            c.close();
17586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
17596802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
17606802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Also query for all social stream item photos.
1761c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        c = db.query(Tables.STREAM_ITEM_PHOTOS + " JOIN " + Tables.STREAM_ITEMS
1762c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " ON " + StreamItemPhotos.STREAM_ITEM_ID + "=" + StreamItemsColumns.CONCRETE_ID
1763c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " JOIN " + Tables.RAW_CONTACTS
1764c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                + " ON " + StreamItems.RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID,
17656802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                new String[]{
1766c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_ID,
1767c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID,
1768c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        StreamItemPhotos.PHOTO_FILE_ID,
1769c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        RawContacts.ACCOUNT_TYPE,
1770c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                        RawContacts.ACCOUNT_NAME
17716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                },
17726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                null, null, null, null, null);
17736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> photoFileIdToStreamItemPhotoId = Maps.newHashMap();
17746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        Map<Long, Long> streamItemPhotoIdToStreamItemId = Maps.newHashMap();
1775c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro        Map<Long, Account> streamItemPhotoIdToAccount = Maps.newHashMap();
17766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
17776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            while (c.moveToNext()) {
17786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemPhotoId = c.getLong(0);
17796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long streamItemId = c.getLong(1);
17806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                long photoFileId = c.getLong(2);
1781c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                String accountType = c.getString(3);
1782c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                String accountName = c.getString(4);
17836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                usedPhotoFileIds.add(photoFileId);
17846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                photoFileIdToStreamItemPhotoId.put(photoFileId, streamItemPhotoId);
17856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                streamItemPhotoIdToStreamItemId.put(streamItemPhotoId, streamItemId);
1786c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                Account account = new Account(accountName, accountType);
1787c2714bbd397b09a20da476c89560e1caecdcce58Dave Santoro                streamItemPhotoIdToAccount.put(photoFileId, account);
1788f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1789f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } finally {
1790f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            c.close();
1791f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1792f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1793f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Run the photo store cleanup.
17945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> missingPhotoIds = mPhotoStore.get().cleanup(usedPhotoFileIds);
1795f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1796d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        // If any of the keys we're using no longer exist, clean them up.  We need to do these
1797d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        // using internal APIs or direct DB access to avoid permission errors.
17986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!missingPhotoIds.isEmpty()) {
1799f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
1800d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.beginTransactionWithListener(this);
1801d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                for (long missingPhotoId : missingPhotoIds) {
1802d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    if (photoFileIdToDataId.containsKey(missingPhotoId)) {
1803d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        long dataId = photoFileIdToDataId.get(missingPhotoId);
1804d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        ContentValues updateValues = new ContentValues();
1805d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        updateValues.putNull(Photo.PHOTO_FILE_ID);
1806d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        updateData(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
1807d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                updateValues, null, null, false);
1808d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    }
1809d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
1810d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        // For missing photos that were in stream item photos, just delete the
1811d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        // stream item photo.
1812d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        long streamItemPhotoId = photoFileIdToStreamItemPhotoId.get(missingPhotoId);
1813d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        db.delete(Tables.STREAM_ITEM_PHOTOS, StreamItemPhotos._ID + "=?",
1814d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                new String[]{String.valueOf(streamItemPhotoId)});
1815d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                    }
1816d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                }
1817d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.setTransactionSuccessful();
1818d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } catch (Exception e) {
1819d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                // Cleanup failure is not a fatal problem.  We'll try again later.
1820d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                Log.e(TAG, "Failed to clean up outdated photo references", e);
1821d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } finally {
1822d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                db.endTransaction();
1823f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
1824f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
1825f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1826f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
18276efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    @Override
1828b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1829b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
183031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
183131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
18326efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    @Override
18336efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected ThreadLocal<ContactsTransaction> getTransactionHolder() {
18346efb7db26598b105342d02207e0ca1c8725c10daDave Santoro        return mTransactionHolder;
18356efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    }
18366efb7db26598b105342d02207e0ca1c8725c10daDave Santoro
18375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public ProfileProvider getProfileProvider() {
18385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return new ProfileProvider(this);
18395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
1841524913c66ce75ca8dec127ac88e3bc2249c246d9Dave Santoro    @VisibleForTesting
1842f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /* package */ PhotoStore getPhotoStore() {
18435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mContactsPhotoStore;
1844f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
1845f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
1846d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    @VisibleForTesting
1847d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    /* package */ PhotoStore getProfilePhotoStore() {
1848d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro        return mProfilePhotoStore;
1849d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro    }
1850d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro
185187614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxDisplayPhotoDim() {
185287614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxDisplayPhotoDim;
185387614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
185487614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
185587614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    /* package */ int getMaxThumbnailPhotoDim() {
185687614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro        return mMaxThumbnailPhotoDim;
185787614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro    }
185887614d7d293b1519dc1f0f403fd59c8bf4d8a347Dave Santoro
1859013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1860013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1861013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1862013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
18635df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
18645df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
18655df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
18665df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
18675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
1868ed78fd6df5e9f3a2d572162e5d374d1f4a625bddDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManagerForTest() {
186972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
187072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
187172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
187272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
18735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
18745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
18755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
18765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
18775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean inProfileMode() {
18785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Boolean profileMode = mInProfileMode.get();
18795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return profileMode != null && profileMode;
18805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
18815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
18823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
18835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(
18845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mContactsHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
1885b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
18863d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18873d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1888568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1889568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1890568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1891568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1892568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1893bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov     * Imports legacy contacts as a background task.
1894568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1895bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    private void importLegacyContactsInBackground() {
1896bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
1897bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
1898568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1899bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
19005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setLocale(this, mCurrentLocale);
1901bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
1902568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1903bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1904bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (importLegacyContacts(importer)) {
1905bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportSuccess();
1906bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        } else {
1907bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            onLegacyContactImportFailure();
1908bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
1909568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1910568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1911bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1912bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
1913bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1914bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
1915bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1916bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
1917bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
1918bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1919b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
19205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
1921b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
1922bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
1923bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
1924bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
1925bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1926bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
1927bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
1928bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
1929bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
1930bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
1931bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
1932bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
1933bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1934bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
1935bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
1936bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
1937bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
1938bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
1939bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
1940bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
1941bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
1942bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
1943bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1944bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
1945bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1946bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
1947bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
1948bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
1949bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        // Do not let any database changes until this issue is resolved.
1950bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        mOkToOpenAccess = false;
19513d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
19523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
19533d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1954568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
19550e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
19563d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
19573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
1958bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
1959bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
1960bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
1961bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
1962bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
1963bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
19643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
19653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
19663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1967bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
1968bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
19693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
19703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1971a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1972a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1973a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1974a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
19755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsHelper.wipeData();
19765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfileHelper.wipeData();
19775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mContactsPhotoStore.clear();
19785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mProfilePhotoStore.clear();
19793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
1980a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1981a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1982568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
198315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov     * During intialization, this content provider will
1984568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1985568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1986568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1987568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1988568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
198915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov    private void waitForAccess(CountDownLatch latch) {
199015c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (latch == null) {
199115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            return;
199215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        }
199315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
199415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        while (true) {
199515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            try {
199615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                latch.await();
199715c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                return;
199815c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov            } catch (InterruptedException e) {
199915c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov                Thread.currentThread().interrupt();
2000ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
2001568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
2002568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2003568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
20045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI should be directed to the profile
20065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database rather than the contacts database.  This is true under either
20075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * of three conditions:
20085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 1. The URI itself is specifically for the profile.
20095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 2. The URI contains ID references that are in the profile ID-space.
20105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * 3. The URI contains lookup key references that match the special profile lookup key.
20115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
20125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB operation to the profile database.
20135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDb(Uri uri) {
20155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return sUriMatcher.mapsToProfile(uri);
20165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Determines whether the given URI with the given values being inserted
20205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * should be directed to the profile database rather than the contacts
20215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * database.  This is true if the URI already maps to the profile DB from
20225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a call to {@link #mapsToProfileDb} or if the URI matches a URI that
20235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * specifies parent IDs via the ContentValues, and the given ContentValues
20245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * contains an ID in the profile ID-space.
20255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param uri The URI to examine.
20265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param values The values being inserted.
20275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @return Whether to direct the DB insert to the profile database.
20285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private boolean mapsToProfileDbWithInsertedValues(Uri uri, ContentValues values) {
20305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
20315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return true;
20325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int match = sUriMatcher.match(uri);
20345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (INSERT_URI_ID_VALUE_MAP.containsKey(match)) {
20355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String idField = INSERT_URI_ID_VALUE_MAP.get(match);
20365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (values.containsKey(idField)) {
20375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = values.getAsLong(idField);
20385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
20395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return true;
20405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
20415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
20425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
20435d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return false;
20445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
20485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a profile operation.
20495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20506efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected void switchToProfileMode() {
20515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mProfileHelper);
20525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mProfileTransactionContext);
20535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mProfileAggregator);
20545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mProfilePhotoStore);
20555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(true);
20565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
20585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
20595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Switches the provider's thread-local context variables to prepare for performing
20605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * a contacts operation.
20615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
20626efb7db26598b105342d02207e0ca1c8725c10daDave Santoro    protected void switchToContactMode() {
20635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mDbHelper.set(mContactsHelper);
20645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.set(mContactTransactionContext);
20655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.set(mContactAggregator);
20665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mPhotoStore.set(mContactsPhotoStore);
20675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mInProfileMode.set(false);
20685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
206936aa19fbab3722288b7f0917166ef6990ab7b52cDave Santoro        // Clear out the active database; modification operations will set this to the contacts DB.
207036aa19fbab3722288b7f0917166ef6990ab7b52cDave Santoro        mActiveDb.set(null);
20715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
20725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2073568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2074568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
207515c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
207636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
207736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
207836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
207936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
20805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDbWithInsertedValues(uri, values)) {
2081078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
2082078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.insert(uri, values);
20835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
20845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
20855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.insert(uri, values);
20865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2087568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2088568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2089568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2090568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
209115c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        if (mWriteAccessLatch != null) {
2092bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2093bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2094bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2095bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2096bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            if (match == PROVIDER_STATUS) {
2097bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2098bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2099bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                    scheduleBackgroundTask(BACKGROUND_TASK_IMPORT_LEGACY_CONTACTS);
2100bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2101bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2102bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2103bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2104bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2105bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
210615c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
210736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
210836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
210936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
211036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
21115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
2112078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
2113078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.update(uri, values, selection, selectionArgs);
21145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
21165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.update(uri, values, selection, selectionArgs);
21175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2118568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2119568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2120568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2121568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
212215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
212336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
212436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
212536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamWritePermission(uri);
212636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
21275d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
2128078f588cef389358adabc579de00747878f3c108Dave Santoro            switchToProfileMode();
2129078f588cef389358adabc579de00747878f3c108Dave Santoro            return mProfileProvider.delete(uri, selection, selectionArgs);
21305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
21315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
21325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return super.delete(uri, selection, selectionArgs);
21335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
21345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
21355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
21365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /**
21375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * Replaces the current (thread-local) database to use for the operation with the given one.
21385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     * @param db The database to use.
21395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro     */
21405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    /* package */ void substituteDb(SQLiteDatabase db) {
21415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
2142568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2143568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2144568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
214582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    public Bundle call(String method, String arg, Bundle extras) {
214682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        waitForAccess(mReadAccessLatch);
214782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (method.equals(Authorization.AUTHORIZATION_METHOD)) {
214882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Uri uri = (Uri) extras.getParcelable(Authorization.KEY_URI_TO_AUTHORIZE);
214982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
215082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Check permissions on the caller.  The URI can only be pre-authorized if the caller
215182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // already has the necessary permissions.
215282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            enforceSocialStreamReadPermission(uri);
215382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            if (mapsToProfileDb(uri)) {
215482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                mProfileProvider.enforceReadPermission(uri);
215582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
215682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
215782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // If there hasn't been a security violation yet, we're clear to pre-authorize the URI.
215882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Uri authUri = preAuthorizeUri(uri);
215982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Bundle response = new Bundle();
216082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            response.putParcelable(Authorization.KEY_AUTHORIZED_URI, authUri);
216182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            return response;
216282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        }
216382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return null;
216482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
216582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
216682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
216782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Pre-authorizes the given URI, adding an expiring permission token to it and placing that
216882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * in our map of pre-authorized URIs.
216982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @param uri The URI to pre-authorize.
217082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @return A pre-authorized URI that will not require special permissions to use.
217182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
217282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    private Uri preAuthorizeUri(Uri uri) {
217382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        String token = String.valueOf(mRandom.nextLong());
217482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        Uri authUri = uri.buildUpon()
217582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                .appendQueryParameter(PREAUTHORIZED_URI_TOKEN, token)
217682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                .build();
217782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        long expiration = SystemClock.elapsedRealtime() + mPreAuthorizedUriDuration;
217882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        mPreAuthorizedUris.put(authUri, expiration);
217982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
218082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return authUri;
218182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
218282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
218382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    /**
218482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * Checks whether the given URI has an unexpired permission token that would grant access to
218582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * query the content.  If it does, the regular permission check should be skipped.
218682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @param uri The URI being accessed.
218782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     * @return Whether the URI is a pre-authorized URI that is still valid.
218882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro     */
218982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    public boolean isValidPreAuthorizedUri(Uri uri) {
219082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        // Only proceed if the URI has a permission token parameter.
219182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (uri.getQueryParameter(PREAUTHORIZED_URI_TOKEN) != null) {
219282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // First expire any pre-authorization URIs that are no longer valid.
219382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            long now = SystemClock.elapsedRealtime();
219482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            Set<Uri> expiredUris = Sets.newHashSet();
219582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            for (Uri preAuthUri : mPreAuthorizedUris.keySet()) {
219682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                if (mPreAuthorizedUris.get(preAuthUri) < now) {
219782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                    expiredUris.add(preAuthUri);
219882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                }
219982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
220082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            for (Uri expiredUri : expiredUris) {
220182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                mPreAuthorizedUris.remove(expiredUri);
220282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
220382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
220482792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Now check to see if the pre-authorized URI map contains the URI.
220582792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            if (mPreAuthorizedUris.containsKey(uri)) {
220682792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                // Unexpired token - skip the permission check.
220782792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                return true;
220882792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            }
220982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        }
221082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        return false;
221182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    }
221282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro
221382792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro    @Override
2214078f588cef389358adabc579de00747878f3c108Dave Santoro    protected boolean yield(ContactsTransaction transaction) {
2215078f588cef389358adabc579de00747878f3c108Dave Santoro        // If there's a profile transaction in progress, and we're yielding, we need to
2216078f588cef389358adabc579de00747878f3c108Dave Santoro        // end it.  Unlike the Contacts DB yield (which re-starts a transaction at its
2217078f588cef389358adabc579de00747878f3c108Dave Santoro        // conclusion), we can just go back into a state in which we have no active
2218078f588cef389358adabc579de00747878f3c108Dave Santoro        // profile transaction, and let it be re-created as needed.  We can't hold onto
2219078f588cef389358adabc579de00747878f3c108Dave Santoro        // the transaction without risking a deadlock.
2220078f588cef389358adabc579de00747878f3c108Dave Santoro        SQLiteDatabase profileDb = transaction.removeDbForTag(PROFILE_DB_TAG);
2221078f588cef389358adabc579de00747878f3c108Dave Santoro        if (profileDb != null) {
2222078f588cef389358adabc579de00747878f3c108Dave Santoro            profileDb.setTransactionSuccessful();
2223078f588cef389358adabc579de00747878f3c108Dave Santoro            profileDb.endTransaction();
2224078f588cef389358adabc579de00747878f3c108Dave Santoro        }
2225078f588cef389358adabc579de00747878f3c108Dave Santoro
2226078f588cef389358adabc579de00747878f3c108Dave Santoro        // Now proceed with the Contacts DB yield.
2227078f588cef389358adabc579de00747878f3c108Dave Santoro        SQLiteDatabase contactsDb = transaction.getDbForTag(CONTACTS_DB_TAG);
2228078f588cef389358adabc579de00747878f3c108Dave Santoro        return contactsDb != null && contactsDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY);
2229078f588cef389358adabc579de00747878f3c108Dave Santoro    }
2230078f588cef389358adabc579de00747878f3c108Dave Santoro
2231078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
2232568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2233568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
223415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mWriteAccessLatch);
2235078f588cef389358adabc579de00747878f3c108Dave Santoro        return super.applyBatch(operations);
2236568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2237568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
22384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
22397b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    public int bulkInsert(Uri uri, ContentValues[] values) {
22407b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov        waitForAccess(mWriteAccessLatch);
2241078f588cef389358adabc579de00747878f3c108Dave Santoro        return super.bulkInsert(uri, values);
22427b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    }
22437b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov
22447b330a64cdf77ddb1c3e7259a7f069e99b025b51Dmitri Plotnikov    @Override
2245078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onBegin() {
2246bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2247b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2248b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
22495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
22505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileAggregator.clearPendingAggregations();
22515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileTransactionContext.clear();
22525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
22535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.clearPendingAggregations();
22545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactTransactionContext.clear();
22555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
2256b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2257b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2258285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2259078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onCommit() {
2260bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2261b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2262b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2263b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
22645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateInTransaction(mTransactionContext.get(), mActiveDb.get());
22651a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
22661a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
22675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateAllVisible();
22681a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
22693826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
2270bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        updateSearchIndexInTransaction();
2271bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
22723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
22733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
22743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
22753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
2276b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2277b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2278078f588cef389358adabc579de00747878f3c108Dave Santoro    @Override
2279078f588cef389358adabc579de00747878f3c108Dave Santoro    public void onRollback() {
2280078f588cef389358adabc579de00747878f3c108Dave Santoro        // Not used.
2281078f588cef389358adabc579de00747878f3c108Dave Santoro    }
2282078f588cef389358adabc579de00747878f3c108Dave Santoro
2283bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    private void updateSearchIndexInTransaction() {
22845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleContacts = mTransactionContext.get().getStaleSearchIndexContactIds();
22855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> staleRawContacts = mTransactionContext.get().getStaleSearchIndexRawContactIds();
2286bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        if (!staleContacts.isEmpty() || !staleRawContacts.isEmpty()) {
2287bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            mSearchIndexManager.updateIndexForRawContacts(staleContacts, staleRawContacts);
22885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().clearSearchIndexUpdates();
2289bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov        }
2290bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov    }
2291bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov
2292b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2293bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2294b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2295b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
22961129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
22975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (long rawContactId : mTransactionContext.get().getInsertedRawContactIds()) {
22985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().updateRawContactDisplayName(mActiveDb.get(), rawContactId);
22995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().onRawContactInsert(mTransactionContext.get(), mActiveDb.get(),
23005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    rawContactId);
230124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
230224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
23035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> dirtyRawContacts = mTransactionContext.get().getDirtyRawContactIds();
2304d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
2305a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2306a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2307d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
2308a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
23095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2310a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2311a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
23125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Set<Long> updatedRawContacts = mTransactionContext.get().getUpdatedRawContactIds();
2313d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
2314a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2315a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2316d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
2317a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
23185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(mSb.toString());
2319b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2320b5a4add17815167d20a90645779df34cdf45280dFred Quintana
23215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Update sync states.
23225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        for (Map.Entry<Long, Object> entry : mTransactionContext.get().getUpdatedSyncStates()) {
2323b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
23245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (mDbHelper.get().getSyncState().update(mActiveDb.get(), id, entry.getValue()) <= 0) {
23259d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
23269d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
23279d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2328b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2329b5a4add17815167d20a90645779df34cdf45280dFred Quintana
23305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().clear();
2331b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2332b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2333a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2334a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2335a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2336a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2337d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
2338b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2339a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2340b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2341a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2342a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2343285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2344285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2345285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2346cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
234781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
234881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
234981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
235081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
235181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
235281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
235381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2354cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2355568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
235651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
23573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
23583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
23593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
23603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
236151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
236251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2363f262d56495ac4ea30d31bd050efb116bd4bb4235Dmitri Plotnikov    public DataRowHandler getDataRowHandler(final String mimeType) {
23645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (inProfileMode()) {
23655d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return getDataRowHandlerForProfile(mimeType);
23665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
23683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
23696d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov            handler = new DataRowHandlerForCustomMimetype(
23705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mContactsHelper, mContactAggregator, mimeType);
23713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
23723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
23733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
23743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
23753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
23765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public DataRowHandler getDataRowHandlerForProfile(final String mimeType) {
23775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        DataRowHandler handler = mProfileDataRowHandlers.get(mimeType);
23785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (handler == null) {
23795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            handler = new DataRowHandlerForCustomMimetype(
23805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    getContext(), mProfileHelper, mProfileAggregator, mimeType);
23815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mProfileDataRowHandlers.put(mimeType, handler);
23825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return handler;
23845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
23855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
23864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2387de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2388bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
23891129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2390b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2391f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
23925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
23935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
2394078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
23955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
23965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
2397f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2398f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2399f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2400a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2401a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
240235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2403a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
240435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
24055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
24065d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mDbHelper.get().getSyncState().insert(mActiveDb.get(), values);
240735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
240835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2409d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2410d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
24116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
24126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
24136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
241424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
241524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                throw new UnsupportedOperationException(
241624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        "The profile contact is created automatically");
241724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
241824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
2419d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
2420d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
24215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = insertRawContact(uri, values, callerIsSyncAdapter);
2422f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2423a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2424a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2425a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2426d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
2427d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
2428d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
2429d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(segment));
2430f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2431f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2432a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2433a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2434a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
24363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItems.RAW_CONTACT_ID, uri.getPathSegments().get(1));
24373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
24383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24420c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
24430c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
2444f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2445f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2446a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2447a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2448a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2449ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2450f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2451f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2452ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2453ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2454ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2455eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
24565aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
245743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2458eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2459eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2460eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
24615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
24625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
246382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
24641f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
24651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
24661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
24673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
24683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItem(uri, values);
24693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
24743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
24753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
24793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
24803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                values.put(StreamItemPhotos.STREAM_ITEM_ID, uri.getPathSegments().get(1));
24813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                id = insertStreamItemPhoto(uri, values);
24823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
24833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
24843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
24853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
2486a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
248781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2488f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2489a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2490a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
24927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
24937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2495de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2496a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2497a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2498a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2499e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2500e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2501e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2502e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2503e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2504e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2505e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2506e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2507e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2508e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2509e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2510e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2511e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
25127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2513e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2514f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2515f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2516e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2517f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2518f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2519f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2520e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2521e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2522e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2523e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2524e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
25255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2526fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2527e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2528e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2529e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2530e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2531e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2532e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2533e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2534e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2535e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2536e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2537e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2538e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
25395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
2540fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2541e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2542e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2543e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2544f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2545f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2546e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2547f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2548f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2549e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2550e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2551f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2552f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2553e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2554f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2555f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2556f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2557f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2558035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2559f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2560e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
25617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
25627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
256443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Resolves the account and builds an {@link AccountWithDataSet} based on the data set specified
256543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * in the URI or values (if any).
256643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param uri Current {@link Uri} being operated on.
256743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * @param values {@link ContentValues} to read and possibly update.
256843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     */
256943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private AccountWithDataSet resolveAccountWithDataSet(Uri uri, ContentValues values) {
25703593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final Account account = resolveAccount(uri, values);
257143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = null;
257243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (account != null) {
257343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
257443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (dataSet == null) {
25753593682b8d9213fde576a0cff54458ad50563980Dave Santoro                dataSet = values.getAsString(RawContacts.DATA_SET);
2576a71dc460ca951c7aca591f3f470c160cde70a1e3Dave Santoro            } else {
25773593682b8d9213fde576a0cff54458ad50563980Dave Santoro                values.put(RawContacts.DATA_SET, dataSet);
257843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
257943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            accountWithDataSet = new AccountWithDataSet(account.name, account.type, dataSet);
258043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        }
258143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountWithDataSet;
258243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    }
258343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
258443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    /**
2585d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
25866bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
25876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
25886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
25896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2590d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2591de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
25926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
25936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
25946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
259524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro     * Inserts an item in the raw contacts table
2596a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2597f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2598f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2599dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
2600a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2601a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
26025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2603f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2604f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2605f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2606f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
260743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
26087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26093d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
26103d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2611f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
26123d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
26133d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
26145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long rawContactId = mActiveDb.get().insert(Tables.RAW_CONTACTS,
26155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                RawContacts.CONTACT_ID, mValues);
2616f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
26175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2618f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2619f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
26205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markNewForAggregation(rawContactId, aggregationMode);
2621285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
26225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Trigger creation of a Contact based on this RawContact at the end of transaction
26235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactInserted(rawContactId, accountWithDataSet);
2624f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2625dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2626dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2627dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2628dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2629dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2630dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2631dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2632dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
26333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2634023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2635a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2636a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2637dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2638dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2639dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2640dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2641dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2642dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2643dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2644dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2645dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
26465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.GROUPS + "," + Tables.RAW_CONTACTS,
26475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROJECTION_GROUP_ID, selection,
2648dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2649dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2650dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2651dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2652dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2653dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2654dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2655dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2656dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2657dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2658dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2659dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2660dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2661dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2662dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2663dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2664dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2665dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2666dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2667dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2668dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2669dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2670dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2671dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2672dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2673dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2674dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2675dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2676dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
26775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
26785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().insert(Tables.DATA, null, groupMembershipValues);
2679dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2680dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2681dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2682dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
26835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Long.toString(mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2684dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2685dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
26865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2687dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2688dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2689a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2690a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2691a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2692a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2693a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2694a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2695f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2696a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2697de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2698de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
269967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2700de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
270120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2702de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2703de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2704de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
27055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
2706de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2707de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2708508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2709de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2710de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2711de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2712de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2713de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
27144097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
27155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.get().getMimeTypeId(mimeType));
2716de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2717a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2718a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
27195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = rowHandler.insert(mActiveDb.get(), mTransactionContext.get(), rawContactId, mValues);
2720f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
27215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().markRawContactDirty(rawContactId);
2722a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
27235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mTransactionContext.get().rawContactUpdated(rawContactId);
2724a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
27254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
27264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
27273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_items table.  The account is checked against the
27293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account in the raw contact for which the stream item is being inserted.  If the
27303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * new stream item results in more stream items under this raw contact than the limit,
27313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest one will be deleted (note that if the stream item inserted was the
27323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * oldest, it will be immediately deleted, and this will return 0).
27333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
27343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
27353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
27363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return the stream item _ID of the newly created row, or 0 if it was not created
27373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItem(Uri uri, ContentValues values) {
27393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
27403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
27413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
27423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = mValues.getAsLong(StreamItems.RAW_CONTACT_ID);
27443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Ensure that the raw contact exists and belongs to the caller's account.
27463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, mValues);
27473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccount(account, rawContactId);
27483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27496802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to insert accounts params - they don't exist in the stream items table.
27506802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_NAME);
27516802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        mValues.remove(RawContacts.ACCOUNT_TYPE);
27526802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
27533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Insert the new stream item.
27545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        id = mActiveDb.get().insert(Tables.STREAM_ITEMS, null, mValues);
27556802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (id == -1) {
27566802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Insertion failed.
27576802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return 0;
27586802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
27593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check to see if we're over the limit for stream items under this raw contact.
27613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // It's possible that the inserted stream item is older than the the existing
27623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // ones, in which case it may be deleted immediately (resetting the ID to 0).
27633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        id = cleanUpOldStreamItems(rawContactId, id);
27643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
27663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
27673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
27693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Inserts an item in the stream_item_photos table.  The account is checked against
27703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the account in the raw contact that owns the stream item being modified.
27713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
27723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param uri the insertion URI
27733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param values the values for the new row
27746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return the stream item photo _ID of the newly created row, or 0 if there was an issue
27756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     with processing the photo or creating the row
27763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
27773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long insertStreamItemPhoto(Uri uri, ContentValues values) {
27783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long id = 0;
27793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.clear();
27803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mValues.putAll(values);
27813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long streamItemId = mValues.getAsLong(StreamItemPhotos.STREAM_ITEM_ID);
27833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (streamItemId != 0) {
27843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            long rawContactId = lookupRawContactIdForStreamId(streamItemId);
27853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            // Ensure that the raw contact exists and belongs to the caller's account.
27873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            Account account = resolveAccount(uri, mValues);
27883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            enforceModifyingAccount(account, rawContactId);
27893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27906802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Don't attempt to insert accounts params - they don't exist in the stream item
27916802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // photos table.
27926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_NAME);
27936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            mValues.remove(RawContacts.ACCOUNT_TYPE);
27943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
27956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Process the photo and store it.
27966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (processStreamItemPhoto(mValues, false)) {
27976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Insert the stream item photo.
27985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                id = mActiveDb.get().insert(Tables.STREAM_ITEM_PHOTOS, null, mValues);
27996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
28003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return id;
28023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
28033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
28056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Processes the photo contained in the {@link ContactsContract.StreamItemPhotos#PHOTO}
28066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * field of the given values, attempting to store it in the photo store.  If successful,
28076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * the resulting photo file ID will be added to the values for insert/update in the table.
28086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * <p>
28096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * If updating, it is valid for the picture to be empty or unspecified (the function will
28106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * still return true).  If inserting, a valid picture must be specified.
28116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param values The content values provided by the caller.
28126802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param forUpdate Whether this photo is being processed for update (vs. insert).
28136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return Whether the insert or update should proceed.
28146802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
28156802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    private boolean processStreamItemPhoto(ContentValues values, boolean forUpdate) {
28166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (!values.containsKey(StreamItemPhotos.PHOTO)) {
28176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
28186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28196802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        byte[] photoBytes = values.getAsByteArray(StreamItemPhotos.PHOTO);
28206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (photoBytes == null) {
28216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return forUpdate;
28226802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
28246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo and store it.
28256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        try {
28265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            long photoFileId = mPhotoStore.get().insert(new PhotoProcessor(photoBytes,
28271dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                    mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim, true), true);
28286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            if (photoFileId != 0) {
28296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.put(StreamItemPhotos.PHOTO_FILE_ID, photoFileId);
28306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                values.remove(StreamItemPhotos.PHOTO);
28316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return true;
28326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            } else {
28336802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // Couldn't store the photo, return 0.
28346802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Log.e(TAG, "Could not process stream item photo for insert");
28356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                return false;
28366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            }
28376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        } catch (IOException ioe) {
28386802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            Log.e(TAG, "Could not process stream item photo for insert", ioe);
28396802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            return false;
28406802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
28416802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
28426802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
28436802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
28443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Looks up the raw contact ID that owns the specified stream item.
28453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param streamItemId The ID of the stream item.
28463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The associated raw contact ID, or -1 if no such stream item exists.
28473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long lookupRawContactIdForStreamId(long streamItemId) {
28493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long rawContactId = -1;
28505d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
28515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItems.RAW_CONTACT_ID},
28523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems._ID + "=?", new String[]{String.valueOf(streamItemId)},
28533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, null);
28543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
28553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (c.moveToFirst()) {
28563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                rawContactId = c.getLong(0);
28573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
28583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
28593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
28603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
28613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return rawContactId;
28623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
28633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
28643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
286536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * If the given URI is reading stream items or stream photos, this will run a permission check
286636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * for the android.permission.READ_SOCIAL_STREAM permission - otherwise it will do nothing.
286736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * @param uri The URI to check.
286836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     */
286936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private void enforceSocialStreamReadPermission(Uri uri) {
287082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro        if (SOCIAL_STREAM_URIS.contains(sUriMatcher.match(uri))
287182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro                && !isValidPreAuthorizedUri(uri)) {
287236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            getContext().enforceCallingOrSelfPermission(
287336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    "android.permission.READ_SOCIAL_STREAM", null);
287436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        }
287536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    }
287636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
287736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    /**
287836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * If the given URI is modifying stream items or stream photos, this will run a permission check
287936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * for the android.permission.WRITE_SOCIAL_STREAM permission - otherwise it will do nothing.
288036612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     * @param uri The URI to check.
288136612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro     */
288236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    private void enforceSocialStreamWritePermission(Uri uri) {
288336612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        if (SOCIAL_STREAM_URIS.contains(sUriMatcher.match(uri))) {
288436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro            getContext().enforceCallingOrSelfPermission(
288536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    "android.permission.WRITE_SOCIAL_STREAM", null);
288636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        }
288736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    }
288836612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
288936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro    /**
28903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given raw contact ID is owned by the given account.
28913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account is null, this will return true iff the raw contact
28923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * is also associated with the "null" account.
28933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *
28943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * If the resolved account does not match, this will throw a security exception.
28953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
28963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to check for.
28973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
28983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void enforceModifyingAccount(Account account, long rawContactId) {
28993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String accountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
29003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + "=? AND "
29013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "=?";
29023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        String noAccountSelection = RawContactsColumns.CONCRETE_ID + "=? AND "
29033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL AND "
29043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
29053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Cursor c;
29063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        if (account != null) {
29075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c = mActiveDb.get().query(Tables.RAW_CONTACTS,
29085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{RawContactsColumns.CONCRETE_ID}, accountSelection,
29093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    new String[]{String.valueOf(rawContactId), mAccount.name, mAccount.type},
29103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
29113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } else {
29125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c = mActiveDb.get().query(Tables.RAW_CONTACTS,
29135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{RawContactsColumns.CONCRETE_ID}, noAccountSelection,
29145d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    new String[]{String.valueOf(rawContactId)},
29153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    null, null, null);
29163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if(c.getCount() == 0) {
29193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                throw new SecurityException("Caller account does not match raw contact ID "
29203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    + rawContactId);
29213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream items matches up with the given
29293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
29303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
29313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
29323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
29333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
29343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item IDs that would be included in this selection.
29353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItems(Account account, String selection,
29373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
29383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = Lists.newArrayList();
29393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItems(qb);
29415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = qb.query(mActiveDb.get(),
29423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{StreamItems._ID, StreamItems.RAW_CONTACT_ID},
29433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
29443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
29463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemIds.add(c.getLong(0));
29473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
29493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
29503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds;
29553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29573b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29583b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Checks whether the given selection of stream item photos matches up with the given
29593b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * account.  If any of the raw contacts fail the account check, this will throw a
29603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * security exception.
29613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param account The resolved account (may be null).
29623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selection The selection.
29633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param selectionArgs The selection arguments.
29643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The list of stream item photo IDs that would be included in this selection.
29653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private List<Long> enforceModifyingAccountForStreamItemPhotos(Account account, String selection,
29673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
29683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemPhotoIds = Lists.newArrayList();
29693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        setTablesAndProjectionMapForStreamItemPhotos(qb);
29715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = qb.query(mActiveDb.get(),
29725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[]{StreamItemPhotos._ID, StreamItems.RAW_CONTACT_ID},
29733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selection, selectionArgs, null, null, null);
29743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
29753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            while (c.moveToNext()) {
29763b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                streamItemPhotoIds.add(c.getLong(0));
29773b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Throw a security exception if the account doesn't match the raw contact's.
29793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                enforceModifyingAccount(account, c.getLong(1));
29803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
29813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
29823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
29833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
29843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemPhotoIds;
29853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
29863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
29873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    /**
29883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * Queries the database for stream items under the given raw contact.  If there are
29893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * more entries than {@link ContactsProvider2#MAX_STREAM_ITEMS_PER_RAW_CONTACT},
29903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * the oldest entries (as determined by timestamp) will be deleted.
29913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param rawContactId The raw contact ID to examine for stream items.
29923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @param insertedStreamItemId The ID of the stream item that was just inserted,
29933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     prompting this cleanup.  Callers may pass 0 if no insertion prompted the
29943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     cleanup.
29953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     * @return The ID of the inserted stream item if it still exists after cleanup;
29963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     *     0 otherwise.
29973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann     */
29983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private long cleanUpOldStreamItems(long rawContactId, long insertedStreamItemId) {
29993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        long postCleanupInsertedStreamId = insertedStreamItemId;
30005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS, new String[]{StreamItems._ID},
30013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
30023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                null, null, StreamItems.TIMESTAMP + " DESC, " + StreamItems._ID + " DESC");
30033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        try {
30043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            int streamItemCount = c.getCount();
30053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            if (streamItemCount <= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
30063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                // Still under the limit - nothing to clean up!
30073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return insertedStreamItemId;
30083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            } else {
30093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                c.moveToLast();
30103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                while (c.getPosition() >= MAX_STREAM_ITEMS_PER_RAW_CONTACT) {
30113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long streamItemId = c.getLong(0);
30123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (insertedStreamItemId == streamItemId) {
30133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        // The stream item just inserted is being deleted.
30143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        postCleanupInsertedStreamId = 0;
30153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
30163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    deleteStreamItem(c.getLong(0));
30173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    c.moveToPrevious();
30183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
30193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
30203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        } finally {
30213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            c.close();
30223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
30233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return postCleanupInsertedStreamId;
30243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
30253b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
30269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
302720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
302820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
3029f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
303020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
303120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3032de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3033de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
30340c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Uri dataUri = inProfileMode()
30350c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                ? Uri.withAppendedPath(Profile.CONTENT_URI, RawContacts.Data.CONTENT_DIRECTORY)
30360c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro                : Data.CONTENT_URI;
30370c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro        Cursor c = query(dataUri, DataRowHandler.DataDeleteQuery.COLUMNS,
3038f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                selection, selectionArgs, null);
3039de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
3040de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
3041f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
3042f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
3043a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
30445d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count += rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
3045f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
30465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mTransactionContext.get().markRawContactDirty(rawContactId);
304788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
304820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
304920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
3050de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
305120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
305220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
305320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
305420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
305520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
305688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
305788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
305888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
305920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
3060f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
306188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
306288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
30634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
3064f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
30654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
3066f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
306720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
306820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
306920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
307020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
307120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3072f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov            String mimeType = c.getString(DataRowHandler.DataDeleteQuery.MIMETYPE);
307320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
307420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
307520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
307620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
307720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
307820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
307920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
308020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
308120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
30827a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
308320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
308420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3085a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
30865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return rowHandler.delete(mActiveDb.get(), mTransactionContext.get(), c);
308720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
308820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
308920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
309020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
309120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
309220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
3093ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
3094ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
3095f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
3096f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
3097f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
3098f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
30993593682b8d9213fde576a0cff54458ad50563980Dave Santoro        final AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues);
3100ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3101ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
3102f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
310367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
31045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
310567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
3106f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
3107ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3108dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
3109dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
3110dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
3111dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3112f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
3113f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
311473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
311573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
31165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        long result = mActiveDb.get().insert(Tables.GROUPS, Groups.TITLE, mValues);
3117ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3118dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
3119dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
3120dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
3121dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
31223593682b8d9213fde576a0cff54458ad50563980Dave Santoro            if (accountWithDataSet == null) {
3123dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
312443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + " IS NULL AND "
312543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + " IS NULL";
3126dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
31273593682b8d9213fde576a0cff54458ad50563980Dave Santoro            } else if (accountWithDataSet.getDataSet() == null) {
3128dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
31293593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
31303593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        + RawContacts.DATA_SET + " IS NULL";
31313593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
31323593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
31333593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType()
31343593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
313543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            } else {
313643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selection = RawContacts.ACCOUNT_NAME + "=? AND "
313743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.ACCOUNT_TYPE + "=? AND "
313843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + RawContacts.DATA_SET + "=?";
31393593682b8d9213fde576a0cff54458ad50563980Dave Santoro                selectionArgs = new String[] {
31403593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountName(),
31413593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getAccountType(),
31423593682b8d9213fde576a0cff54458ad50563980Dave Santoro                        accountWithDataSet.getDataSet()
31433593682b8d9213fde576a0cff54458ad50563980Dave Santoro                };
3144dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
31455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3146dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
3147dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
3148892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
3149892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
3150892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
3151892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
3152892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
31535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mTransactionContext.get().markRawContactDirty(rawContactId);
3154892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
3155dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3156892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
3157892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
3158dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3159dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3160dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3161f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
31621a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3163ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
3164ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3165ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
3166ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3167ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31685aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
3169f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // Before inserting, ensure that no settings record already exists for the
3170f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // values being inserted (this used to be enforced by a primary key, but that no
3171f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // longer works with the nullable data_set field added).
3172f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountName = values.getAsString(Settings.ACCOUNT_NAME);
3173f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String accountType = values.getAsString(Settings.ACCOUNT_TYPE);
3174f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        String dataSet = values.getAsString(Settings.DATA_SET);
3175f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Uri.Builder settingsUri = Settings.CONTENT_URI.buildUpon();
3176f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountName != null) {
3177f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_NAME, accountName);
3178f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3179f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (accountType != null) {
3180f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.ACCOUNT_TYPE, accountType);
3181f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3182f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        if (dataSet != null) {
3183f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            settingsUri.appendQueryParameter(Settings.DATA_SET, dataSet);
3184f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3185f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        Cursor c = queryLocal(settingsUri.build(), null, null, null, null, 0);
3186f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        try {
3187f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (c.getCount() > 0) {
31880e21a867a572679d64d79041eb574d13665178d4Dave Santoro                // If a record was found, replace it with the new values.
31890e21a867a572679d64d79041eb574d13665178d4Dave Santoro                String selection = null;
31900e21a867a572679d64d79041eb574d13665178d4Dave Santoro                String[] selectionArgs = null;
31910e21a867a572679d64d79041eb574d13665178d4Dave Santoro                if (accountName != null && accountType != null) {
31920e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    selection = Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=?";
31930e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    if (dataSet == null) {
31940e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selection += " AND " + Settings.DATA_SET + " IS NULL";
31950e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selectionArgs = new String[] {accountName, accountType};
31960e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    } else {
31970e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selection += " AND " + Settings.DATA_SET + "=?";
31980e21a867a572679d64d79041eb574d13665178d4Dave Santoro                        selectionArgs = new String[] {accountName, accountType, dataSet};
31990e21a867a572679d64d79041eb574d13665178d4Dave Santoro                    }
32000e21a867a572679d64d79041eb574d13665178d4Dave Santoro                }
32010e21a867a572679d64d79041eb574d13665178d4Dave Santoro                return updateSettings(uri, values, selection, selectionArgs);
3202f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            }
3203f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        } finally {
3204f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            c.close();
3205f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        }
3206f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro
3207f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        // If we didn't find a duplicate, we're fine to insert.
32085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long id = mActiveDb.get().insert(Tables.SETTINGS, null, values);
32095aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
32101a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
32111a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3212e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
32131a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
3214e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
3215e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3216e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3217ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
321882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
32191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
322082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
322182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
32220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
32234dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
32244dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
32250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
322682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
32274dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
32284dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
32294dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
32304dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
32311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
32321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3233dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3234dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
323582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
32366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountType = null;
32376802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        String accountName = null;
3238f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
32392526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3240dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3241dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3242dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32432526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
32442526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
32451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3246dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3247dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
32490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
32500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
32510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3252dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3253dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3254dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
32555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String mimeTypeIdIm = String.valueOf(mDbHelper.get().getMimeTypeIdForIm());
3256dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
32575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String mimeTypeIdEmail = String.valueOf(mDbHelper.get().getMimeTypeIdForEmail());
3258f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3259f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3260f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3261f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3262f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3263f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3264f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
32652526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
32662526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
32672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
32682526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
32692526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32702526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
32712526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3273dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32742526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32752526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3276dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
32772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
32782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3279dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
32802526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
32812526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
32822526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
32832526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32842526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
32852526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3286dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32872526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32882526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3289dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3290dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
32911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
329282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
32932526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
32942526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3295dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
329670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
329770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
32981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
32991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
33005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = mActiveDb.get().query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
33012526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
33024394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
33031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
330467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
33055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
33066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountType = cursor.getString(DataContactsQuery.ACCOUNT_TYPE);
33076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                accountName = cursor.getString(DataContactsQuery.ACCOUNT_NAME);
3308e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
33091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
33101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
33111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
33121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
33131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
331431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
331531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
331631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
33171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
33181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
331982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3320a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3321a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3322a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3323a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3324a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3325a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3326a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
332782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3328a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3329a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
333082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
333182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
333282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
333382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
333482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3335a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
333682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
333782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3338aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3339aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
33401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3341a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
33425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().replace(Tables.PRESENCE, null, mValues);
3343a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3344e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
33450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
334682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
334782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
33480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
33490bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Resources resources = getContext().getResources();
33500bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!TextUtils.isEmpty(resPackage)) {
33510bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                PackageManager pm = getContext().getPackageManager();
33520bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                try {
33530bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    resources = pm.getResourcesForApplication(resPackage);
33540bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                } catch (NameNotFoundException e) {
33550bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                    Log.w(TAG, "Contact status update resource package not found: "
33560bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            + resPackage);
33570bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                }
33580bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
33590bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer labelResourceId = values.getAsInteger(StatusUpdates.STATUS_LABEL);
33600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33610bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if ((labelResourceId == null || labelResourceId == 0) && protocol != null) {
33620bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                labelResourceId = Im.getProtocolLabelResource(protocol);
33630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
33640bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String labelResource = getResourceName(resources, "string", labelResourceId);
33650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33660bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            Integer iconResourceId = values.getAsInteger(StatusUpdates.STATUS_ICON);
33670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
33680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33690bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            String iconResource = getResourceName(resources, "drawable", iconResourceId);
33700bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
3371a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
33725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().deleteStatusUpdate(dataId);
3373a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
33746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                Long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
33756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (timestamp != null) {
33765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().replaceStatusUpdate(dataId, timestamp, status, resPackage,
33770bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            iconResourceId, labelResourceId);
33786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                } else {
33795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().insertStatusUpdate(dataId, status, resPackage, iconResourceId,
33800bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                            labelResourceId);
33816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
33826802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33836802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // For forward compatibility with the new stream item API, insert this status update
33846802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // there as well.  If we already have a stream item from this source, update that
33856802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // one instead of inserting a new one (since the semantics of the old status update
33866802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                // API is to only have a single record).
33876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                if (rawContactId != -1 && !TextUtils.isEmpty(status)) {
33886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    ContentValues streamItemValues = new ContentValues();
33896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3390d5ef5903570e533a501abe6a8e3d533fdb5318fcFlavio Lerda                    // Status updates are text only but stream items are HTML.
3391e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda                    streamItemValues.put(StreamItems.TEXT, statusUpdateToHtml(status));
33926802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.COMMENTS, "");
33936802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_PACKAGE, resPackage);
33946802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_ICON, iconResource);
33956802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.RES_LABEL, labelResource);
33966802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    streamItemValues.put(StreamItems.TIMESTAMP,
33976802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            timestamp == null ? System.currentTimeMillis() : timestamp);
33986802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
33996802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Note: The following is basically a workaround for the fact that status
34006802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates didn't do any sort of account enforcement, while social stream item
34016802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // updates do.  We can't expect callers of the old API to start passing account
34026802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // information along, so we just populate the account params appropriately for
340343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // the raw contact.  Data set is not relevant here, as we only check account
340443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // name and type.
34056802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    if (accountName != null && accountType != null) {
34066802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName);
34076802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType);
34086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
34096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
34106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    // Check for an existing stream item from this source, and insert or update.
34116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    Uri streamUri = StreamItems.CONTENT_URI;
341236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                    Cursor c = queryLocal(streamUri, new String[]{StreamItems._ID},
34136802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            StreamItems.RAW_CONTACT_ID + "=?",
341436612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            new String[]{String.valueOf(rawContactId)},
341536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            null, -1 /* directory ID */);
34166802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    try {
34176802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        if (c.getCount() > 0) {
34186802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                            c.moveToFirst();
341936612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            updateInTransaction(ContentUris.withAppendedId(streamUri, c.getLong(0)),
34206802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                                    streamItemValues, null, null);
34216802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        } else {
342236612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro                            insertInTransaction(streamUri, streamItemValues);
34236802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        }
34246802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    } finally {
34256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                        c.close();
34266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                    }
34276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                }
3428e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3429e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3430bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3431a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
34325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mAggregator.get().updateLastStatusUpdateId(contactId);
3433a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3434a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3435a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
34361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
34371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3438e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    /** Converts a status update to HTML. */
3439e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    private String statusUpdateToHtml(String status) {
34404747809486541f7a3d342d3e1dd48fb5ea255ad6Flavio Lerda        return TextUtils.htmlEncode(status);
3441e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda    }
3442e3a10e4fcdb8b5e619f02a26fd1a26cef3b149a3Flavio Lerda
34430bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    private String getResourceName(Resources resources, String expectedType, Integer resourceId) {
34440bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        try {
34450bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (resourceId == null || resourceId == 0) return null;
34460bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34470bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            // Resource has an invalid type (e.g. a string as icon)? ignore
34480bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceEntryName = resources.getResourceEntryName(resourceId);
34490bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            final String resourceTypeName = resources.getResourceTypeName(resourceId);
34500bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            if (!expectedType.equals(resourceTypeName)) {
34510bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                Log.w(TAG, "Resource " + resourceId + " (" + resourceEntryName + ") is of type " +
34520bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                        resourceTypeName + " but " + expectedType + " is required.");
34530bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                return null;
34540bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            }
34550bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34560bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return resourceEntryName;
34570bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        } catch (NotFoundException e) {
34580bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann            return null;
34590bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann        }
34600bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann    }
34610bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann
34624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3463de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3464bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3465b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3466b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
34675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
34685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
34695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
3470078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
34715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
34725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
3473b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3474f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3475f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3476508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3477508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
347835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
34795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
34805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selection,
34815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
34835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case SYNCSTATE_ID: {
34845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
34855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
34865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
34875d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().delete(mActiveDb.get(), selectionWithId,
34885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
349035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
34915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
3492b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3493b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3494b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
34955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().delete(mActiveDb.get(), selectionWithId,
34965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs);
34975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
3498b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3499cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3500cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3501cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3502cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3503cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3504d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3505d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3506dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
35076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
35086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
35099fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
35102e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
35112e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
35122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
35135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3514fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
35152e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
35162e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
35175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3518dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
35192e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
35202e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
35219fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
35229fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
35239fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
35249fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
35259fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
35269fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3527a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
35289fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
35299fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
35309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
35319fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
35329fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
35339fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
35349fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
35359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
353660de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
35379fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
35385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = query(mActiveDb.get(), lookupQb, null, selection, args, null, null,
35395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
35409fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
35419fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
35429fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3543dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
35449fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
35459fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
35469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
35479fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
35489fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
35499fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
35509fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
35519fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
35529fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
35539fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
3554d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
3555d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
35562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
35575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3558fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3559e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
35602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
35612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
35622971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3563fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3564fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3565fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
35662971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
35672971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
35682971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
35692971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
35702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
35712971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
35722971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
3573d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
3574d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
35752971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
35765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return deleteRawContact(rawContactId, mDbHelper.get().getContactId(rawContactId),
3577fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3578508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3579508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
35800c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
35810c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
3582f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3583944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3584f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
358520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
358620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
358748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
358848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
358948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
3590d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case POSTALS_ID:
3591d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
3592508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3593f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
35944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
35954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3596ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3597ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3598ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3599f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
36005aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
36012971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
36022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
36032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
36042971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
36055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups._ID},
3606e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
36072971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
36082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
36095aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
36102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
36112971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
36122971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
36132971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
361481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3615f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
361681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
36172971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3618508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3619508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3620eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
362143880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3622e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3623eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3624eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
36255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
36265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
36270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
36281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
36291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
36303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
36313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(), selection, selectionArgs);
36333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
36363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItems(uri, new ContentValues(),
36389b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                        StreamItems._ID + "=?",
36393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
36403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
364282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
364382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                mSyncToNetwork |= !callerIsSyncAdapter;
364482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
364582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
364682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                return deleteStreamItems(uri, new ContentValues(),
364782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
364882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
364982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
365082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
365182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
36523b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
36533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36545d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String streamItemId = uri.getPathSegments().get(1);
36555d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                String selectionWithId =
36565d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        (StreamItemPhotos.STREAM_ITEM_ID + "=" + streamItemId + " ")
36575d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                                + (selection == null ? "" : " AND (" + selection + ")");
36585d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                return deleteStreamItemPhotos(uri, new ContentValues(),
36595d9fcbaaa0007134564d63272470296f5d23b62aDave Santoro                        selectionWithId, selectionArgs);
36603b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36613b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
36623b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
36633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                mSyncToNetwork |= !callerIsSyncAdapter;
36643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
36653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
36663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return deleteStreamItemPhotos(uri, new ContentValues(),
36673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND "
36683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                + StreamItemPhotos.STREAM_ITEM_ID + "=?",
36693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
36703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
36713b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
367281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
367381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
36743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
367581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3676508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
36774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
36784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36791c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3680ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
36815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final long groupMembershipMimetypeId = mDbHelper.get()
368294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
36835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
368494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
368594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
368694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
368794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3688f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
36895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
369094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
369194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
369294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3693f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
36945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId,
36955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        null);
369694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
369794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
36981a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
369994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
370094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
370194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
37025aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
37035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().delete(Tables.SETTINGS, selection, selectionArgs);
37041a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3705e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3706e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3707e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3708dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
370996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
37105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
371196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
371296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3713cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3714cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3715cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3716dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3717cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3718cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3719cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3720cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3721cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
37223826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
37233826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
37245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3725cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3726cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3727fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
37285d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
37293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
37303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
373182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        // Find and delete stream items associated with the raw contact.
373282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        Cursor c = mActiveDb.get().query(Tables.STREAM_ITEMS,
373382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                new String[]{StreamItems._ID},
373482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
373582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                null, null, null);
373682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        try {
373782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            while (c.moveToNext()) {
373882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                deleteStreamItem(c.getLong(0));
373982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
374082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        } finally {
374182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            c.close();
374282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro        }
374382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
3744d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        if (callerIsSyncAdapter || rawContactIsLocal(rawContactId)) {
37455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().delete(Tables.PRESENCE,
37465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
37475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            int count = mActiveDb.get().delete(Tables.RAW_CONTACTS,
37485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    RawContacts._ID + "=" + rawContactId, null);
374941f76a59a31946f6d784dacf9f13d9a4c0bbe203Dave Santoro            mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
3750fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
375133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
37525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().removeContactIfSingleton(rawContactId);
3753dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
375433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
375533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
375633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3757d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    /**
3758d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     * Returns whether the given raw contact ID is local (i.e. has no account associated with it).
3759d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro     */
3760d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    private boolean rawContactIsLocal(long rawContactId) {
3761d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        Cursor c = mActiveDb.get().query(Tables.RAW_CONTACTS,
3762d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {
3763d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.ACCOUNT_NAME,
3764d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.ACCOUNT_TYPE,
3765d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                        RawContacts.DATA_SET
3766d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                },
3767d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                RawContacts._ID + "=?",
3768d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                new String[] {String.valueOf(rawContactId)}, null, null, null);
3769d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        try {
3770d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            return c.moveToFirst() && c.isNull(0) && c.isNull(1) && c.isNull(2);
3771d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        } finally {
3772d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            c.close();
3773d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro        }
3774d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro    }
3775d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro
37760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
37779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
37789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
37799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
37809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
37819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
37825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      mActiveDb.get().delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
37839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
37845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro      return mActiveDb.get().delete(Tables.PRESENCE, selection, selectionArgs);
37850a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
37860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
37873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItems(Uri uri, ContentValues values, String selection,
37883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
37893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream items to be deleted, and check that they belong
37903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // to the account.
37913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
37923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        List<Long> streamItemIds = enforceModifyingAccountForStreamItems(
37933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                account, selection, selectionArgs);
37943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
37953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
37963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        for (long streamItemId : streamItemIds) {
37973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            deleteStreamItem(streamItemId);
37983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        }
37993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        mVisibleTouched = true;
38013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        return streamItemIds.size();
38023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItem(long streamItemId) {
38053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
38063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        deleteStreamItemPhotos(streamItemId);
38075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEMS, StreamItems._ID + "=?",
38083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
38093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(Uri uri, ContentValues values, String selection,
38123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
38133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // First query for the stream item photos to be deleted, and check that they
38143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // belong to the account.
38153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
38163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
38173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If no security exception has been thrown, we're fine to delete.
38195d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS, selection, selectionArgs);
38203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
38223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int deleteStreamItemPhotos(long streamItemId) {
38233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Note that this does not enforce the modifying account.
38245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().delete(Tables.STREAM_ITEM_PHOTOS,
38255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                StreamItemPhotos.STREAM_ITEM_ID + "=?",
38263b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                new String[]{String.valueOf(streamItemId)});
38273b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
38283b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
3829dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
383081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
383181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3832cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3833cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3834cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3835cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3836cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3837cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3838dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3839cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3840cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
38414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3842de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3843de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3844bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3845b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3846b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3847b5a4add17815167d20a90645779df34cdf45280dFred Quintana
38485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
38495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
3850078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getWritableDatabase());
38515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
38525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
385335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
385400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
385500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3856b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3857b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
38581129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
38595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mTransactionContext.get().syncStateUpdated(rowId, data);
3860b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3861b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3862b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3863f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3864f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
386500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
386635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
38675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
38685d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
3869b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3870b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3871b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3872b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3873b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3874b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3875b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
38765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().update(mActiveDb.get(), values,
38775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionWithId, selectionArgs);
38785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
38795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
38805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE_ID: {
38815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection = appendAccountToSelection(uri, selection);
38825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                String selectionWithId =
38835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
38845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        + (selection == null ? "" : " AND (" + selection + ")");
38855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mProfileHelper.getSyncState().update(mActiveDb.get(), values,
3886b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3887b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
388835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3889d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case CONTACTS:
3890d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE: {
3891dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
389200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
389300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
389400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3895d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3896dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3897c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3898c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3899c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
39002e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
39012e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
39022e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
39032e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
39042e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
39055d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
3906fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
39072e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
39082e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
39095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
3910dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
39112e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
39122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
39132e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
3914d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
3915d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
3916d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
3917d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                final String rawContactId = uri.getPathSegments().get(segment);
39187d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
39197d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
39207d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
39217d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
39227d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
39237d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
39247d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
39257d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
39260c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case DATA:
39270c5812a467378c57c2d2715ee4f0a9f541c64809Dave Santoro            case PROFILE_DATA: {
3928944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3929f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
393081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3931f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
393281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
393320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
393420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3935c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
393648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
393748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
393848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
393948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3940f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
394181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3942f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
394381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
394400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
394500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
39467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case RAW_CONTACTS:
39485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_RAW_CONTACTS: {
39495ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3950dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
39517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
39527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
395533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
39564529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
39574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
39584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3959dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3960dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
39614529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
39624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3963dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3964dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
39654529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
39667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
39677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3969ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
39705aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3971f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
397281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3973f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
397481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3975ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3976ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3977ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3978ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3979ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
39804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
39814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
398273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
39835aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
39845aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
398581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3986f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
398781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3988ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3989ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3990ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3991127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
39925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                count = updateAggregationException(mActiveDb.get(), values);
3993b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3994b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3995b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3996eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3997e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3998e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
399943880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
4000eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
4001eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
4002eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
40035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
40045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
40059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
40069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
40079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
40089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
40103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItems(uri, values, selection, selectionArgs);
40113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
40159b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                count = updateStreamItems(uri, values, StreamItems._ID + "=?",
40163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{uri.getLastPathSegment()});
40173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
402082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
402182780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String rawContactId = uri.getPathSegments().get(1);
402282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                String streamItemId = uri.getLastPathSegment();
402382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                count = updateStreamItems(uri, values,
402482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems.RAW_CONTACT_ID + "=? AND " + StreamItems._ID + "=?",
402582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        new String[]{rawContactId, streamItemId});
402682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
402782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
402882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
40293b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
40303b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values, selection, selectionArgs);
40313b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40323b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40333b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40343b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
40353b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
40363b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
40373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{streamItemId});
40383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
40413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
40423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
40433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
40443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                count = updateStreamItemPhotos(uri, values,
40453b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=? AND " +
40463b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                                StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?",
40473b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        new String[]{streamItemPhotoId, streamItemId});
40483b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
40493b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
40503b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
405172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
4052bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov                mContactDirectoryManager.scanPackagesByUid(Binder.getCallingUid());
405372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
4054d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4055d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4056d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
405746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            case DATA_USAGE_FEEDBACK_ID: {
405846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (handleDataUsageFeedback(uri)) {
405946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 1;
406046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } else {
406146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    count = 0;
406246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
406346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                break;
406446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
406546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
406681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
406781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
4068f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
406981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
407000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
407100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
407200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
40734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
40744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
40769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
40779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
40789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
40799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
40809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
40819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
40825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.STATUS_UPDATES,
40839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
40849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
40859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
40869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
40879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
40899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
40909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
40915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro          updateCount = mActiveDb.get().update(Tables.PRESENCE, settableValues,
40929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
40939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
40949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
40959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
40969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
40979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
40989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
40993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItems(Uri uri, ContentValues values, String selection,
41003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
41013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream items can't be moved to a new raw contact.
41023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItems.RAW_CONTACT_ID);
41033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream items being updated belong to the account.
41053b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
41063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItems(account, selection, selectionArgs);
41073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41086802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream items table.
41096802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
41106802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
41116802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
41123b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // If there's been no exception, the update should be fine.
41135d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        return mActiveDb.get().update(Tables.STREAM_ITEMS, values, selection, selectionArgs);
41143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
41153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private int updateStreamItemPhotos(Uri uri, ContentValues values, String selection,
41173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            String[] selectionArgs) {
41183b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Stream item photos can't be moved to a new stream item.
41193b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        values.remove(StreamItemPhotos.STREAM_ITEM_ID);
41203b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41213b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        // Check that the stream item photos being updated belong to the account.
41223b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        Account account = resolveAccount(uri, values);
41233b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        enforceModifyingAccountForStreamItemPhotos(account, selection, selectionArgs);
41243b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41256802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Don't attempt to update accounts params - they don't exist in the stream item
41266802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // photos table.
41276802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_NAME);
41286802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        values.remove(RawContacts.ACCOUNT_TYPE);
41296802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
41306802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        // Process the photo (since we're updating, it's valid for the photo to not be present).
41316802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (processStreamItemPhoto(values, true)) {
41326802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // If there's been no exception, the update should be fine.
41335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mActiveDb.get().update(Tables.STREAM_ITEM_PHOTOS, values, selection,
41345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selectionArgs);
41356802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        }
41366802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return 0;
41373b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
41383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
41399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
41409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
41419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
41429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
41439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
41449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
41459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
41469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
41479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
41489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
41519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
41529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
41539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
41549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
41559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
41569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
41579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
41589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
41599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
41609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
41619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
41629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
41639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
41669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
41679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
41689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
4169aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
4170aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
41719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
41729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
41739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
41745aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
4175f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
417673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4177ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
4178ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
417973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
4180f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
418173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
418273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
418373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
418473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
418573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
418673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
418773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
418873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
41895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.GROUPS, updatedValues, selectionWithId,
41905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selectionArgs);
41911a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
41921a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
419394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
419443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
419543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // TODO: This will not work for groups that have a data set specified, since the content
419643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // resolver will not be able to request a sync for the right source (unless it is updated
419743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        // to key off account with data set).
41986ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
41991129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
42005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor c = mActiveDb.get().query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
4201e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
42026ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
42036ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
42046ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
42056ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
42066ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
42076ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
42086ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
420924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
42106ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
4211ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
42126ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
42136ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
42146ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
42156ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
42166ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
42176ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
42186ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
42196ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
422094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
422194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
422294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
4223b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
4224b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
42255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final int count = mActiveDb.get().update(Tables.SETTINGS, values, selection, selectionArgs);
42261a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
42271a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
4228e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
4229e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
4230e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
4231e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4232dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
4233dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
42344529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
42354529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
42364529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
42374529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
423873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
423997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
424097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
424197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
424297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
424397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
42444529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
42455d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
424651bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
42474529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
42484529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
42494529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
42504529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
4251dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
42524529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
42534529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
42544529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
42554529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
42564529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
42574529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
42584529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
42594529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
42604529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
4261dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
4262dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
426396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
426496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
426519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
426619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
426719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
4268ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
4269ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
427043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String dataSet = null;
427119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
42725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
42735d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    selection, mSelectionArgs1, null, null, null);
427419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
427519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
427619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
4277ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
4278ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
427943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    dataSet = cursor.getString(RawContactsQuery.DATA_SET);
428019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
428119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
428219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
428319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
428419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
428519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
428619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
4287f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
42885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int count = mActiveDb.get().update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
42895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
4290f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
4291f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
4292f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
4293f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
4294f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
4295f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
42965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().markForAggregation(rawContactId, aggregationMode, false);
4297f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
4298f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
4299433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
4300dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
4301dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4302dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
4303dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
43045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateStarred(rawContactId);
4305dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
4306dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
4307dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
4308dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
4309dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
4310dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
43115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    boolean starred = 0 != DatabaseUtils.longForQuery(mActiveDb.get(),
4312dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
4313dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
4314dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
4315dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4316dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4317dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4318dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
4319dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
4320dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
4321dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
4322433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
4323dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
4324285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
43255d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateLookupKeyForRawContact(mActiveDb.get(), rawContactId);
4326285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
4327f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
4328f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
4329f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
4330f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
4331f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
43325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mDbHelper.get().resetNameVerifiedForOtherRawContacts(rawContactId);
4333f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
43345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mAggregator.get().updateDisplayNameForRawContact(mActiveDb.get(), rawContactId);
4335f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
433619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
43375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mTransactionContext.get().rawContactInserted(rawContactId,
433843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new AccountWithDataSet(accountName, accountType, dataSet));
433919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
43405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
43415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
434233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
434333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
4344321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
4345f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
434620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
434720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
434820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
43495ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
435020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
435120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
435220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
435320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
435420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
43555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
435620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
435720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
435897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
435997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
436097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
436197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
436297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
4363653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
436420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4365653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
4366653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
43675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = queryLocal(uri,
4368f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                DataRowHandler.DataUpdateQuery.COLUMNS,
43695d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                selection, selectionArgs, null, -1 /* directory ID */);
4370653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
4371653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
4372f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
437320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
4374653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
4375653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
437620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
437720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4378653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
437920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
438020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
4381f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
4382653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
4383653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
4384321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
4385653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
4386f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        final String mimeType = c.getString(DataRowHandler.DataUpdateQuery.MIMETYPE);
4387a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
4388f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean updated =
43895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                rowHandler.update(mActiveDb.get(), mTransactionContext.get(), values, c,
43905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        callerIsSyncAdapter);
4391f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
4392f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
4393a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
4394f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return updated ? 1 : 0;
4395321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
4396321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
43978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
4398dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
43998c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
44005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor cursor = mActiveDb.get().query(Views.CONTACTS,
44015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                new String[] { Contacts._ID }, selection, selectionArgs, null, null, null);
44028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
44038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
44048c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
440524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
4406dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
44078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
44088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
44098c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
44108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
44118c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
44128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
44138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
44148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
44158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4416dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
4417dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
4418d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
44198c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4420b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
4421d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4422b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
4423d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4424b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
4425d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4426b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
4427d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4428b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
4429d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
4430d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4431d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
44328c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
4433d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
4434d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
4435d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
44368c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
4437c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
44388c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
4439c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
4440c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
44414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
44425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.get().update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
444397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
44448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4445dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
44465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = mActiveDb.get().query(Views.RAW_CONTACTS,
4447dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
4448dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
4449dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
4450dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4451dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4452dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4453dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4454dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4455dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4456dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4457dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4458dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4459dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
44608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
44618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
44628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4463b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
44648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4465b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
44668c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4467b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
44688c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4469b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
44708c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4471b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
44728c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
44738c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
44745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int rslt = mActiveDb.get().update(Tables.CONTACTS, mValues, Contacts._ID + "=?",
44755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mSelectionArgs1);
44766e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
44779b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
44789b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
44795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
44805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
44819b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
44829b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4483f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4484d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4485127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4486127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
44870c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
44880c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
448980c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
4490ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId1;
4491ed089fd34d7b3baf29709eb4f2bc14fa35117660Dmitri Plotnikov        long rawContactId2;
44920c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
44930c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
44940c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
44950c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
44960c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
44970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4498b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4499127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
45000c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
45014da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
45024da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
45030c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
45044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
45054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
45060c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
45076bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
45086bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
45090c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
45100c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
45110c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
45120c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4513127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4514127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
45155d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().invalidateAggregationExceptionCache();
45165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId1,
451769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
45185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().markForAggregation(rawContactId2,
451969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4520dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
45215d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId1);
45225d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().aggregateContact(mTransactionContext.get(), db, rawContactId2);
4523127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4524127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4525127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4526127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4527b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4528b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
452970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4530bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
45313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
45323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4533bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected boolean updateAccountsInBackground(Account[] accounts) {
4534f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4535e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
45365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
45375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(db);
45385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        db.beginTransaction();
453970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
454043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> existingAccountsWithDataSets =
454143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.ACCOUNTS);
4542743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
454343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Add a row to the ACCOUNTS table (with no data set) for each new account.
4544743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
454543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
454643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        account.name, account.type, null);
454743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!existingAccountsWithDataSets.contains(accountWithDataSet)) {
4548e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
454943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
455043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    // Add an account entry with an empty data set to match the account.
45515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
455243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
455343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + ") VALUES (?, ?, ?)",
455443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new String[] {
455543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
455643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
455743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
455843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            });
4559743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4560743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
456148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
456243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Check each of the existing sub-accounts against the account list.  If the owning
456343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // account no longer exists, the sub-account and all its data should be deleted.
456443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<AccountWithDataSet> accountsWithDataSetsToDelete =
456543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    new ArrayList<AccountWithDataSet>();
456643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            List<Account> accountList = Arrays.asList(accounts);
456743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : existingAccountsWithDataSets) {
456843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                Account owningAccount = new Account(
456943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountWithDataSet.getAccountName(), accountWithDataSet.getAccountType());
457043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                if (!accountList.contains(owningAccount)) {
457143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSetsToDelete.add(accountWithDataSet);
457243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                }
457370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
457470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
457543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            if (!accountsWithDataSetsToDelete.isEmpty()) {
4576e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
457743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
457843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    Log.d(TAG, "removing data for removed account " + accountWithDataSet);
457943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountParams = new String[] {
458043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountName(),
458143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            accountWithDataSet.getAccountType()
458243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    };
458343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String[] accountWithDataSetParams = accountWithDataSet.getDataSet() == null
458443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            ? accountParams
458543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            : new String[] {
458643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountName(),
458743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getAccountType(),
458843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    accountWithDataSet.getDataSet()
458943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            };
459043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String groupsDataSetClause = " AND " + Groups.DATA_SET
459143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
459243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    String rawContactsDataSetClause = " AND " + RawContacts.DATA_SET
459343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
4594f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                    String settingsDataSetClause = " AND " + Settings.DATA_SET
4595f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?");
459643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
45975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4598e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4599e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
460043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + Groups.ACCOUNT_TYPE + " = ?" +
460143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    groupsDataSetClause, accountWithDataSetParams);
46025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4603e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4604e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4605e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4606e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4607e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
460843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
460943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                    rawContactsDataSetClause + ")", accountWithDataSetParams);
46105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4611c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            "DELETE FROM " + Tables.STREAM_ITEM_PHOTOS +
4612c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            " WHERE " + StreamItemPhotos.STREAM_ITEM_ID + " IN (" +
4613c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    "SELECT " + StreamItems._ID +
4614c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " FROM " + Tables.STREAM_ITEMS +
4615c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
4616c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            "SELECT " + RawContacts._ID +
4617c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " FROM " + Tables.RAW_CONTACTS +
4618c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4619c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
4620c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                            rawContactsDataSetClause + "))",
4621c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            accountWithDataSetParams);
4622c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                    db.execSQL(
4623c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            "DELETE FROM " + Tables.STREAM_ITEMS +
4624c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            " WHERE " + StreamItems.RAW_CONTACT_ID + " IN (" +
4625c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    "SELECT " + RawContacts._ID +
4626c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " FROM " + Tables.RAW_CONTACTS +
4627c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4628c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
4629c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                                    rawContactsDataSetClause + ")",
4630c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                            accountWithDataSetParams);
4631c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                    db.execSQL(
4632e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4633e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
463443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?" +
463543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
46365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4637e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4638e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4639f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            " AND " + Settings.ACCOUNT_TYPE + " = ?" +
4640f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                            settingsDataSetClause, accountWithDataSetParams);
46415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4642e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4643e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
464443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + RawContacts.ACCOUNT_TYPE + "=?" +
464543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            rawContactsDataSetClause, accountWithDataSetParams);
46465d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    db.execSQL(
4647d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4648d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
464943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            " AND " + Directory.ACCOUNT_TYPE + "=?", accountParams);
46504458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4651e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4652e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
465333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
465433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4655e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
465633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
46575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = db.rawQuery("SELECT " + Contacts._ID +
465833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
465933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
466069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
466169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
466269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
466333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
466433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
466569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
466669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
466733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
466833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
466933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
467033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
467133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
467233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
467333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
467433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
467533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
46765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mAggregator.get().updateAggregateData(mTransactionContext.get(), contactId);
467733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
46785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().updateAllVisible();
4679bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov                updateSearchIndexInTransaction();
468033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
468133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
468243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Now that we've done the account-based additions and subtractions from the Accounts
468343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // table, check for raw contacts that have been added with a data set and add Accounts
468443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // entries for those if necessary.
468543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            existingAccountsWithDataSets = findValidAccountsWithDataSets(Tables.ACCOUNTS);
468643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            Set<AccountWithDataSet> rawContactAccountsWithDataSets =
468743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    findValidAccountsWithDataSets(Tables.RAW_CONTACTS);
468843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            rawContactAccountsWithDataSets.removeAll(existingAccountsWithDataSets);
468943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
469043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // Any remaining raw contact sub-accounts need to be added to the Accounts table.
469143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            for (AccountWithDataSet accountWithDataSet : rawContactAccountsWithDataSets) {
469243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                accountsChanged = true;
469343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
469443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // Add an account entry to match the raw contact.
46955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
469643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET
469743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        + ") VALUES (?, ?, ?)",
469843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        new String[] {
469943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountName(),
470043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getAccountType(),
470143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                                accountWithDataSet.getDataSet()
470243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        });
470343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
470443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro
4705e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
470643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                // TODO: Should sync state take data set into consideration?
47075d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().getSyncState().onAccountsChanged(db, accounts);
4708e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
47095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
471070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
47115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.endTransaction();
471270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
471373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
47143826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
47153826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
47163826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
47173826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
47183826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
47193826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4720afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
472170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4722619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
47233826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
47243826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
47253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
47263826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
47273826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
47283826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
47293826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
47303826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
47313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
47323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
47333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
47343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
47353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
47363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
47373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
47383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
47393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
47403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
47413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
47423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
474372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
4744bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_DIRECTORIES, packageName);
4745d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4746d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4747619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
474843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Finds all distinct account types and data sets present in the specified table.
4749627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
475043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    private Set<AccountWithDataSet> findValidAccountsWithDataSets(String table) {
475143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Set<AccountWithDataSet> accountsWithDataSets = new HashSet<AccountWithDataSet>();
47525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = mActiveDb.get().rawQuery(
475343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "SELECT DISTINCT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
475443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                "," + RawContacts.DATA_SET +
475543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                " FROM " + table, null);
4756627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4757627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
475891abbc9f691297594262d1f2d79acb744a66712cDave Santoro                if (!c.isNull(0) && !c.isNull(1)) {
475943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    accountsWithDataSets.add(
476043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                            new AccountWithDataSet(c.getString(0), c.getString(1), c.getString(2)));
4761627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4762627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4763627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4764627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4765627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
476643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        return accountsWithDataSets;
4767627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4768627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
47694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
47704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
47714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
477215c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
477315c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov        waitForAccess(mReadAccessLatch);
477415c6e903b6d66e20e7cb1ebe7ff8c713e0a3386bDmitri Plotnikov
477536612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        // Enforce stream items access check if applicable.
477636612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro        enforceSocialStreamReadPermission(uri);
477736612112760df799ef89f7e324e5dfabd5ca0d2bDave Santoro
47785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Query the profile DB if appropriate.
47795d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
47805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
47815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mProfileProvider.query(uri, projection, selection, selectionArgs, sortOrder);
47825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
47835d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
47845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Otherwise proceed with a normal query against the contacts DB.
47855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        switchToContactMode();
47865d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mActiveDb.set(mContactsHelper.getReadableDatabase());
4787d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4788385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
4789b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1));
4791385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
4792b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47933716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
47945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            Directory.DEFAULT));
4795d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
4796b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri,
47973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    queryLocal(uri, projection, selection, selectionArgs, sortOrder,
47985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            Directory.LOCAL_INVISIBLE));
4799d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4800d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4801d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4802d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4803a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4804a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4805d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4806d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4807d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4808d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4809d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4810d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4811d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4812d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4813d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4814d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4815d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4816d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
48172e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
48182e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
48192e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
48202e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
48212e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
48222e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4823d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
482409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
482509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
482609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
482709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
482809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4829332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4830d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
48316ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
48326ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (cursor == null) {
48336ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
48346ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
48356ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
4836547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        CrossProcessCursor crossProcessCursor = getCrossProcessCursor(cursor);
4837547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (crossProcessCursor != null) {
4838b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return addSnippetExtrasToCursor(uri, cursor);
4839547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        } else {
4840b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return matrixCursorFromCursor(addSnippetExtrasToCursor(uri, cursor));
4841547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
48423716f1447ceb21180d1301790eabd8b9453f486dDave Santoro    }
48433716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4844b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addSnippetExtrasToCursor(Uri uri, Cursor cursor) {
4845547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
4846547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        // If the cursor doesn't contain a snippet column, don't bother wrapping it.
4847547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        if (cursor.getColumnIndex(SearchSnippetColumns.SNIPPET) < 0) {
4848b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            return cursor;
4849547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro        }
4850547c5eddcc79f680dc128b3851bf6cc03b0d0ebfDave Santoro
48513716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // Parse out snippet arguments for use when snippets are retrieved from the cursor.
48523716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String[] args = null;
48533716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String snippetArgs =
48543716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
48553716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        if (snippetArgs != null) {
48563716f1447ceb21180d1301790eabd8b9453f486dDave Santoro            args = snippetArgs.split(",");
48573716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        }
48583716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
48593716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String query = uri.getLastPathSegment();
48603716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String startMatch = args != null && args.length > 0 ? args[0]
48613716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_START_MATCH;
48623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String endMatch = args != null && args.length > 1 ? args[1]
48633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_END_MATCH;
48643716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        String ellipsis = args != null && args.length > 2 ? args[2]
48653716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_ELLIPSIS;
48663716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
48673716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
48683716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
4869b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        // Snippet data is needed for the snippeting on the client side, so store it in the cursor
4870b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor && deferredSnippetingRequested(uri)){
4871b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4872b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4873b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4874b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4875b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4876b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putString(ContactsContract.DEFERRED_SNIPPETING_QUERY, query);
4877b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4878b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
48795517770250b3afa4fd88b6869c3244680821d222Dave Santoro        }
4880b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
4881b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
4882b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
4883b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private Cursor addDeferredSnippetingExtra(Cursor cursor) {
4884b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (cursor instanceof AbstractCursor){
4885b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle oldExtras = cursor.getExtras();
4886b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            Bundle extras = new Bundle();
4887b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            if (oldExtras != null) {
4888b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                extras.putAll(oldExtras);
4889b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            }
4890b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            extras.putBoolean(ContactsContract.DEFERRED_SNIPPETING, true);
4891b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            ((AbstractCursor) cursor).setExtras(extras);
4892b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
4893b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return cursor;
48946ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
48956ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
48966ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    private CrossProcessCursor getCrossProcessCursor(Cursor cursor) {
48976ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        Cursor c = cursor;
48986ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        if (c instanceof CrossProcessCursor) {
48996ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return (CrossProcessCursor) c;
49006ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else if (c instanceof CursorWindow) {
49016ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return getCrossProcessCursor(((CursorWrapper) c).getWrappedCursor());
49026ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        } else {
49036ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            return null;
49046ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        }
49056ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    }
49066ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov
49076ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov    public MatrixCursor matrixCursorFromCursor(Cursor cursor) {
49086ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
49096ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        int numColumns = cursor.getColumnCount();
49106ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        String data[] = new String[numColumns];
49116ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        cursor.moveToPosition(-1);
49126ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        while (cursor.moveToNext()) {
49136ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            for (int i = 0; i < numColumns; i++) {
49146ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov                data[i] = cursor.getString(i);
49156ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            }
49166ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov            newCursor.addRow(data);
4917332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
49186ca827312e6c69bdd88b48e485debee9fe5d16baDmitri Plotnikov        return newCursor;
4919d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4920d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4921d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4922d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4923d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4924d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4925d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4926d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4927d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4928d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4929d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4930d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4931d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4932d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4933d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4934d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4935d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4936d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4937d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4938d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
49394458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
49404458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
49414458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
49425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
494349d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
49444458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
49454458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
49464458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
49474458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
49484458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
49494458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
49504458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
49514458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
49524458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
49534458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
49544458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
49554458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
49564458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4957d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
49584458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4959d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4960d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
49614458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
49624458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4963d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4964d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
496572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
49664458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
49674458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
49684458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
496972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
497072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
49715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    protected Cursor queryLocal(Uri uri, String[] projection, String selection,
49725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            String[] selectionArgs, String sortOrder, long directoryId) {
4973bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4974bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4975bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
49760b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
49775d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
49785d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
4979078f588cef389358adabc579de00747878f3c108Dave Santoro            mActiveDb.set(mContactsHelper.getReadableDatabase());
49805d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
498135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4982d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
49831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4984c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4985b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean snippetDeferred = false;
4986c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
49872ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // The expression used in bundleLetterCountExtras() to get count.
49882ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        String addressBookIndexerCountExpression = null;
49892ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
4990a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
49914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
499235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
49935d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_SYNCSTATE:
49945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mDbHelper.get().getSyncState().query(mActiveDb.get(), projection, selection,
49955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        selectionArgs, sortOrder);
499635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4997d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4998763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
49994b64b6e8f448938434cb1e022a4e7dfaae8f9c8cMakoto Onuki                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
5000619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
5001619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
5002619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5003d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
50044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
5005763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
50064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
50074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
50086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
50096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
50106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
50115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
50125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
50135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
50145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
50155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
50165d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
5017fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
50185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
5019a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
50205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
50215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
50225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
50235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
5024763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
5025a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
50265d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5027a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5028a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
5029a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
50305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
50315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
50325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
50335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5034763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
50354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
50365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
50374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
50385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
50395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
50405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50412149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
5042bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_DATA:
5043bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
5044bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO: {
50452149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
50462149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
50472149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
50485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
50492149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
50502149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
50512149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
50522149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
50532149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
50542149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
50552149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
5056bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
5057bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
5058bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
5059a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
50605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5061a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5062a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
5063a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
50642149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
50652149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
50662149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50672149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
50682149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
50692149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50702149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
50715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
50722149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
507324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
5074bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                if (match == CONTACTS_LOOKUP_PHOTO || match == CONTACTS_LOOKUP_ID_PHOTO) {
5075bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
5076bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                }
50772149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
50782149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
50792149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
50802149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
50813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_ID_STREAM_ITEMS: {
50823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long contactId = Long.parseLong(uri.getPathSegments().get(1));
50833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
50843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5085af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                qb.appendWhere(StreamItems.CONTACT_ID + "=?");
50863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
50873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
50883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
50893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_STREAM_ITEMS:
50903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
50913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                List<String> pathSegments = uri.getPathSegments();
50923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                int segmentCount = pathSegments.size();
50933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount < 4) {
50945d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
50953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            "Missing a lookup key", uri));
50963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
50973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String lookupKey = pathSegments.get(2);
50983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                if (segmentCount == 5) {
50993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    long contactId = Long.parseLong(pathSegments.get(3));
51003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
51013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    setTablesAndProjectionMapForStreamItems(lookupQb);
51025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
51033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5104af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_ID, contactId,
5105af10329f85c5d8c4196c495a9f0f9a6c6ecbc231Daniel Lehmann                            StreamItems.CONTACT_LOOKUP_KEY, lookupKey);
51063b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    if (c != null) {
51073b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        return c;
51083b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                    }
51093b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                }
51103b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
51113b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
51125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
51133b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
51143b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
51153b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
51163b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
51173b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5118f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
511942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
51205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
5121ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
5122f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
51234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
512424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        String.valueOf(contactId));
51254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
5126f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
5127f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
5128f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
512942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
513042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
513142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
51325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mActiveDb.get().rawQuery(
513342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
513442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
513542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
513642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
513742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
513842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
5139ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
5140916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
5141b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                boolean deferredSnipRequested = deferredSnippetingRequested(uri);
5142ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
5143916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
5144ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
51457ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(
5146b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        qb, uri, projection, filterParam, directoryId,
5147b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        deferredSnipRequested);
5148b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                snippetDeferred = isSingleWordQuery(filterParam) &&
5149b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                        deferredSnipRequested && snippetNeeded(projection);
5150897e51661e0cb08c0f2d3e59136a1a6f8ceb2316Daisuke Miyakawa                // Omit results in "Other Contacts".
5151897e51661e0cb08c0f2d3e59136a1a6f8ceb2316Daisuke Miyakawa                qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
5152ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5153ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5154ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
5155ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
5156ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
51572f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Basically the resultant SQL should look like this:
51582f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing starred items)
51592f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
51602f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // (SQL for listing frequently contacted items)
51612f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // ORDER BY ...
51622f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
51632f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final boolean phoneOnly = readBooleanQueryParameter(
51642f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
51652f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
51664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
51674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5168e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
51695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
51702f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
51714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
51724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
51732f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] subProjection = null;
51745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
51752f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
51765e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
51775e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
51784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
51794928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
51804928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                qb.setProjectionMap(phoneOnly ?
51814928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        sStrequentPhoneOnlyStarredProjectionMap
51824928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                        : sStrequentStarredProjectionMap);
51839dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                if (phoneOnly) {
51845d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    qb.appendWhere(DbQueryUtils.concatenateClauses(
51855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            selection, Contacts.HAS_PHONE_NUMBER + "=1"));
51869dbfd650ccf93714f3266e80f9fbdbcb526ae7b3Daisuke Miyakawa                }
51872f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
51882f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String starredQuery = qb.buildQuery(subProjection,
518924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
5190d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
51912f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Reset the builder.
5192d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
51932f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                qb.setStrict(true);
51944928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
51954928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                // Build the second query for frequent part.
51964928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                final String frequentQuery;
51974928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                if (phoneOnly) {
51984928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    final StringBuilder tableBuilder = new StringBuilder();
51994928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // In phone only mode, we need to look at view_data instead of
52004928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // contacts/raw_contacts to obtain actual phone numbers. One problem is that
52014928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data is much larger than view_contacts, so our query might become much
52024928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // slower.
52034928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    //
52044928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // To avoid the possible slow down, we start from data usage table and join
52054928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // view_data to the table, assuming data usage table is quite smaller than
52064928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // data rows (almost always it should be), and we don't want any phone
52074928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // numbers not used by the user. This way sqlite is able to drop a number of
52084928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    // rows in view_data in the early stage of data lookup.
52094928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    tableBuilder.append(Tables.DATA_USAGE_STAT
52104928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
52114928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
52124928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataColumns.CONCRETE_ID + " AND "
52134928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
52144928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
52154928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
52164928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    appendContactStatusUpdateJoin(tableBuilder, projection,
52174928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            ContactsColumns.LAST_STATUS_UPDATE_ID);
52184928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa
52194928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setTables(tableBuilder.toString());
52204928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
52214928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
52224928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
52234928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
52244928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            MimetypesColumns.MIMETYPE + " IN ("
52254928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + Phone.CONTENT_ITEM_TYPE + "', "
52264928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            + "'" + SipAddress.CONTENT_ITEM_TYPE + "')"));
52274928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection, null, null, null, null, null);
52284928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                } else {
52294928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
52304928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
52314928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    qb.appendWhere(DbQueryUtils.concatenateClauses(
52324928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            selection,
52335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)"));
52344928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                    frequentQuery = qb.buildQuery(subProjection,
52354928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            null, Contacts._ID, null, null, null);
52364928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                }
5237d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
5238d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
52392f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                final String unionQuery =
52402f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
52412f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
52422f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
52432f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // Here, we need to use selection / selectionArgs (supplied from users) "twice",
52442f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // as we want them both for starred items and for frequently contacted items.
52452f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                //
52462f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // e.g. if the user specify selection = "starred =?" and selectionArgs = "0",
52472f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // the resultant SQL should be like:
52482f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
52492f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // UNION ALL
52502f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                // SELECT ... WHERE starred =? AND ...
52512f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                String[] doubledSelectionArgs = null;
52522f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (selectionArgs != null) {
52532f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    final int length = selectionArgs.length;
52542f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    doubledSelectionArgs = new String[length * 2];
52557d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
52567d7d0e95636344c01eb4e4d034791c199bee98e9Daisuke Miyakawa                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
52572f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                }
52582f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
52595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor cursor = mActiveDb.get().rawQuery(unionQuery, doubledSelectionArgs);
52602f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                if (cursor != null) {
52612f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    cursor.setNotificationUri(getContext().getContentResolver(),
5262d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
5263d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
52642f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                return cursor;
5265d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
5266d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
526745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            case CONTACTS_FREQUENT: {
526845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
526945ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                qb.setProjectionMap(sStrequentFrequentProjectionMap);
527045ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                groupBy = Contacts._ID;
527145ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                if (!TextUtils.isEmpty(sortOrder)) {
527245ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
527345ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                } else {
527445ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                    sortOrder = FREQUENT_ORDER_BY;
527545ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                }
527645ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa                break;
527745ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa            }
527845ae7eaf0e2c9459ccbeeb5eb5977f055c4ed8ecDaisuke Miyakawa
5279ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
5280763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
5281b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
528271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
52837cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    String groupMimeTypeId = String.valueOf(
52847cf50494501938f175d288077145acf49da8f171Daniel Lehmann                            mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
52854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
52867cf50494501938f175d288077145acf49da8f171Daniel Lehmann                    selectionArgs = insertSelectionArg(selectionArgs, groupMimeTypeId);
5287b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
5288b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
5289b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
5290b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
529124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE: {
529224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
529324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
529424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
529524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
529624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES: {
529724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForEntities(qb, uri, projection);
529824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
529924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
530024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
530124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD: {
5302ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
530324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                qb.setProjectionMap(sContactsVCardProjectionMap);
530424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
530524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
530624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5307a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
53084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
530982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
53114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
53126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
53136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
531400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
5315a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
53163653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
531782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
53184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
53194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
53203653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
53213653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
53223653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
53233653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
5324a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
5325a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
5326a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5327a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
5328a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
5329a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5330a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5331a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5332a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
5333a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
5334a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
5335a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
5336a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
53375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
5338a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
5339a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5340a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
5341a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
5342a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
5343a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
5344a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
5345a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
5346a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
53475d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
5348a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
5349a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
5350a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
5351a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
5352a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
5353a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
5354a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
5355a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5356a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
5357a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
53585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        String.valueOf(lookupContactIdByLookupKey(mActiveDb.get(), lookupKey)));
5359a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
5360a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
5361a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
5362a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
53633b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS: {
53643b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
53653b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53663b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID: {
53693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
53703b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
53719b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann                qb.appendWhere(StreamItems._ID + "=?");
53723b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53733b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53743b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53753b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_LIMIT: {
53766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
53776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
53783b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                return cursor;
53793b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53803b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53813b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_PHOTOS: {
53823b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
53833b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53843b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53853b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53863b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS: {
53873b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
53883b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
53893b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
53903b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
53913b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
53923b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
53933b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
53943b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case STREAM_ITEMS_ID_PHOTOS_ID: {
53953b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItemPhotos(qb);
53963b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemId = uri.getPathSegments().get(1);
53973b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                String streamItemPhotoId = uri.getPathSegments().get(3);
53983b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
53993b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
54003b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
54013b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
54023b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
54033b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
54043b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
5405f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case PHOTO_DIMENSIONS: {
5406f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                MatrixCursor cursor = new MatrixCursor(
5407f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
5408f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        1);
5409f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
5410f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return cursor;
5411f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
5412f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
54134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
541482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54157cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + "=" +
54167cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        mDbHelper.get().getMimeTypeIdForPhone());
54172ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
54182ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // Dedupe phone numbers per contact.
5419cf55cbe8932f620484a3634d13ecc116c32fdc99Daisuke Miyakawa                groupBy = RawContacts.CONTACT_ID + ", " + Data.DATA1;
54202ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
54212ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // In this case, because we dedupe phone numbers, the address book indexer needs
54222ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // to take it into account too.  (Otherwise headers will appear in wrong positions.)
54232ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // So use count(distinct pair(CONTACT_ID, PHONE NUMBER)) instead of count(*).
54242ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // But because there's no such thing as pair() on sqlite, we use
54252ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // CONTACT_ID || ',' || PHONE NUMBER instead.
54262ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                // This only slows down the query by 14% with 10,000 contacts.
54272ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                addressBookIndexerCountExpression = "DISTINCT "
54282ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                        + RawContacts.CONTACT_ID + "||','||" + Data.DATA1;
54292815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
54302815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
54312815f58f72f109790585931f601a63ddc02536a5Evan Millar
543248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
543382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
54344da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
54357cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54367cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForPhone());
54374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
543848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
543948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
544048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5441ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
544246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
544346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
544446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
544546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
544646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
544746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
54487cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
54497cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForPhone());
5450ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
54514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
54524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
5453a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
54545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
545545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
54565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
54575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
54585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
5459155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN " +
5460155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5461155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5462155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5463155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5464155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5465155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
54662352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
5467155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
54685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
546945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
54705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
54715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5472892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
5473892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
54745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
54755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
54765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
54775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
5478892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
5479892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
5480892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
5481892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
5482892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
548345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
548445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
548545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
548645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
548745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
548845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
548945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
54905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
54915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5492a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
5493ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
549458567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                groupBy = "(CASE WHEN " + PhoneColumns.NORMALIZED_NUMBER
549558567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " IS NOT NULL THEN " + PhoneColumns.NORMALIZED_NUMBER
549658567abca253f1efa2db5c39e17e42dca589e916Daisuke Miyakawa                        + " ELSE " + Phone.NUMBER + " END), " + RawContacts.CONTACT_ID;
5497a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
549846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
549946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
550046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
550146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
550246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = PHONE_FILTER_SORT_ORDER;
550346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
5504a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
5505ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5506ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5507ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
55084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
550982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55107cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55117cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
55124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
55134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
55144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
551548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
551682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
55187cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55197cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail()
55204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
552148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
552248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
552348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
55245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
552582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55267cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
55277cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForEmail());
55284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
552908768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
55305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    String address = mDbHelper.get().extractAddressFromEmailAddress(email);
553108768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
553208768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
55334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
5534ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5535ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5536ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
55375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
553846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
553946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                Integer typeInt = sDataUsageTypeMap.get(typeParam);
554046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                if (typeInt == null) {
554146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
554246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
554346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
554407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
55457d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa
554607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
554707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
554807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
554907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
555007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
555107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
55525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
555307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
555407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
555507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
555607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
555707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
555807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
555907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
556007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
556107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
55622a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
55635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    sb.append(mDbHelper.get().getMimeTypeIdForEmail());
55642a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
556507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
556620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
5567155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(
5568155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " UNION SELECT " + Data._ID +
5569155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.DATA +
5570155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
55715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        sb.append(mDbHelper.get().getMimeTypeIdForEmail());
5572155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
5573155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
5574155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " FROM " + Tables.SEARCH_INDEX +
5575155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " JOIN " + Tables.RAW_CONTACTS +
5576155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
5577155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
5578155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
55792352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
5580155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                        sb.append(")");
55815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
55825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
5583a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
55845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
55855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
5586a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
558746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
558846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
558946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
55907d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    } else {
55917d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                        sortOrder = EMAIL_FILTER_SORT_ORDER;
55927d82ae92714f2132e3a0971d844ae8cdf10d76e7Daisuke Miyakawa                    }
5593a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
55945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
55955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
55965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
5597ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
559882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
55997cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
56007cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
5601ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
5602ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
5603ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
560448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
560582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
56077cf50494501938f175d288077145acf49da8f171Daniel Lehmann                qb.appendWhere(" AND " + DataColumns.MIMETYPE_ID + " = "
56087cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        + mDbHelper.get().getMimeTypeIdForStructuredPostal());
56094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
561048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
561148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
561248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
5613d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS:
5614d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS: {
5615763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
56164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
56174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
56184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5619d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_ID:
5620d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID: {
56215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
5622763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
56234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
56254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
56264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
56274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5628d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case RAW_CONTACTS_DATA:
5629d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_RAW_CONTACTS_ID_DATA: {
5630d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                int segment = match == RAW_CONTACTS_DATA ? 1 : 2;
5631d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(segment));
563282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56344da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
563524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                break;
563624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            }
563724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
56383b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            case RAW_CONTACTS_ID_STREAM_ITEMS: {
56393b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
56403b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                setTablesAndProjectionMapForStreamItems(qb);
56413b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
56423b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
56433b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann                break;
56443b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann            }
564524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
564682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            case RAW_CONTACTS_ID_STREAM_ITEMS_ID: {
564782780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
564882780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                long streamItemId = Long.parseLong(uri.getPathSegments().get(3));
564982780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                setTablesAndProjectionMapForStreamItems(qb);
565082780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(streamItemId));
5651c88cc79e0e19b8299a2a356c7d70b48f70b4a93eDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
565282780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=? AND " +
565382780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                        StreamItems._ID + "=?");
565482780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro                break;
565582780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro            }
565682780691f1a3b4d8784e29a961b1140cd07bc9a8Dave Santoro
565724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
565824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
565924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
566024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                setTablesAndProjectionMapForRawEntities(qb, uri);
56615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                qb.appendWhere(" AND " + RawContacts._ID + "=?");
5662e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5663e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5664e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5665d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA:
5666d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA: {
566782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
5668e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
5669e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
5670e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
5671d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case DATA_ID:
5672d9e353f4a13154dace037c99eb1054d85cce2521Dave Santoro            case PROFILE_DATA_ID: {
567382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
56744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
56754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
5676a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
5677a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
5678a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
567985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            case PROFILE_PHOTO: {
568085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
568185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
568285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                break;
568385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            }
568485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro
5685a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
56864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5687a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
5688a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
5689a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
5690892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
5691a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
5692a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5693e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
5694e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
56955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().getCurrentCountryIso());
5696892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
5697892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
56985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mDbHelper.get().buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
5699e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
5700e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
5701e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
5702e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
5703a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
5704a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
5705a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5706ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
5707ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5708ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
5709f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5710ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5711ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5712ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5713ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
5714ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.GROUPS);
5715ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
57164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
57174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
5718ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5719ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5720ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5721ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
5722f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                final boolean returnGroupCountPerAccount =
5723f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
5724f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                                false);
5725f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setTables(Views.GROUPS + " AS " + Tables.GROUPS);
5726f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                qb.setProjectionMap(returnGroupCountPerAccount ?
5727f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        sGroupsSummaryProjectionMapWithGroupCountPerAccount
5728f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                        : sGroupsSummaryProjectionMap);
5729f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5730f1efadb1255fd75305b59802f736905b9d66e449Daisuke Miyakawa                groupBy = GroupsColumns.CONCRETE_ID;
5731ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
5732ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
5733ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5734b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
57350c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
5736b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
5737b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
5738b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
5739b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
574031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
5741d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
57422d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
57432d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
57442d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
57452d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
574631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
5747d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
5748d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
574931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
575031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
575131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
575231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
57535b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
57545b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
57555b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
57565b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
57575b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
57585b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
57595b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
57605b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
576176dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
57625b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
57635b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
57645b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
57655b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
57665b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
57675b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
57685b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
5769763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
57707581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
57715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return mAggregator.get().queryAggregationSuggestions(qb, projection, contactId,
57725b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
577331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
577431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
5775eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
5776eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
5777eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
5778f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                appendAccountFromParameter(qb, uri);
5779e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5780e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
5781e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
57825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final String groupMembershipMimetypeId = Long.toString(mDbHelper.get()
5783e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
578482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
57855d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(projection, Settings.UNGROUPED_COUNT)) {
5786e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5787e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
578882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
57895d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().isInProjection(
57905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                                projection, Settings.UNGROUPED_WITH_PHONES)) {
5791e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
5792e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
5793e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
5794eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
5795eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
5796eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
57975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case STATUS_UPDATES:
57985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            case PROFILE_STATUS_UPDATES: {
57990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
58005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
58015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
58025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
580382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
58040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
58054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
58064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
58075ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
58085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
58095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
5810c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
5811174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
58125d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), uri, projection, limit);
5813c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5814c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5815c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
58162d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
5817174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                String filter = getQueryParameter(
5818174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
5819174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(
58205d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get(), projection, lookupKey, filter);
5821c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5822c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
58231b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
5824ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
58251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
58261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
58271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
58281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
5829ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
58301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
58311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
58321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
58331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
58341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
5835ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
58361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
58371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
58381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
58391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
58401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
5841ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                qb.setTables(Views.CONTACTS);
58421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
584371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
58447cf50494501938f175d288077145acf49da8f171Daniel Lehmann                String groupMimeTypeId = String.valueOf(
58457cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        mDbHelper.get().getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
58461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
58477cf50494501938f175d288077145acf49da8f171Daniel Lehmann                selectionArgs = insertSelectionArg(selectionArgs, groupMimeTypeId);
58481b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
58491b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
58503202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case RAW_CONTACT_ENTITIES:
58513202ae2de5c5fec9f5f61003a0e6b608283e1961Dave Santoro            case PROFILE_RAW_CONTACT_ENTITIES: {
5852a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
585346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
585446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
585546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
585646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
585746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
5858a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
58594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
58604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
586146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
586246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
586346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
586409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
586509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
586609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
586709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5868d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
5869d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5870d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5871d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5872d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5873d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
5874d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
5875385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
5876d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
5877d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5878385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
5879d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5880d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5881d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5882d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
58837a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
58847a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
58857a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
58867a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
58874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5888f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5889c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
58904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
58914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
589209e69522745551522c55dff27424496f255def46Daniel Lehmann        qb.setStrict(true);
58937f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
5894ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
58955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                query(mActiveDb.get(), qb, projection, selection, selectionArgs, sortOrder, groupBy,
58965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        limit);
5897ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
58985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            cursor = bundleLetterCountExtras(cursor, mActiveDb.get(), qb, selection,
58992ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                    selectionArgs, sortOrder, addressBookIndexerCountExpression);
5900ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5901b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetDeferred) {
5902b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            cursor = addDeferredSnippetingExtra(cursor);
5903b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        }
5904ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
59055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
59065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
59075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
59085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
59095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5910038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5911038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5912038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5913038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
59145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
59155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
59164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
59174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
59184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
59194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
59204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
59214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
592209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
592309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
592409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
592509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
592609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
592709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
592809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
592909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
593009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
593109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
593209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
593309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
593409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
593509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
593609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
593709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5938a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5939a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5940a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5941a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5942a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5943a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5944a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5945a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5946a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5947a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5948a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5949a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5950a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5951a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5952a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5953a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5954a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5955a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5956a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5957a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5958a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
5959a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
5960a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5961a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5962a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5963a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5964a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5965a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5966a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
596709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5968bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
5969bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
5970bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
5971bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
5972ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5973bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
5974bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
5975ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
5976ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5977bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
5978bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
5979bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
5980bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
59815d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // The first letter of the sort key column is what is used for the index headings.
59825d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        public static final String SECTION_HEADING = "SUBSTR(%1$s,1,1)";
598324c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
5984de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
5985ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5986ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5987ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
5988ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
5989ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
5990ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
5991ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
59922ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder,
59932ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            String countExpression) {
5994409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki        if (!(cursor instanceof AbstractCursor)) {
5995409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            Log.w(TAG, "Unable to bundle extras.  Cursor is not AbstractCursor.");
5996409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            return cursor;
5997409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki        }
5998ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
5999ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6000ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
6001ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
6002ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
6003ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
6004ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
6005ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
6006ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
6007ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
6008ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
6009ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
6010ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
6011ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
6012ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
6013ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
6014ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
6015ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6016bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
6017ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
60185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        String sectionHeading = String.format(AddressBookIndexQuery.SECTION_HEADING, sortKey);
6019bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
602024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                sectionHeading + " AS " + AddressBookIndexQuery.LETTER);
6021bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
60222ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        // If "what to count" is not specified, we just count all records.
60232ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        if (TextUtils.isEmpty(countExpression)) {
60242ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki            countExpression = "*";
60252ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki        }
60262ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki
6027bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
6028bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
6029bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
6030bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
6031bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
6032bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
6033bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
6034ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
603524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                "GET_PHONEBOOK_INDEX(" + sectionHeading + ",'" + locale + "')"
6036bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
6037ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
60382ebc62b692be3aaaeafa01d658ae3cdfb25b728aMakoto Onuki                "COUNT(" + countExpression + ") AS " + AddressBookIndexQuery.COUNT);
6039ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
6040ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6041f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
6042ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
6043ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
6044ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6045ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
6046f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
6047ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
6048ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
6049bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
6050bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
6051bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6052bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
6053bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
6054bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
6055ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
6056f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
6057bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
6058bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
6059bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
6060bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
6061bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
6062bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
6063bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
6064bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
6065bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
6066bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
6067bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6068bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
6069bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
6070bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
6071bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
6072bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
6073bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
6074bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
6075bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
6076ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
6077ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
6078409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            final Bundle bundle = new Bundle();
6079409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
6080409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
6081409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki
6082409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            ((AbstractCursor) cursor).setExtras(bundle);
6083409605a187683155d9c6dbc2626b6419e3dd384eMakoto Onuki            return cursor;
6084ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
6085f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
6086ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
6087ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
6088ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
60892d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
609092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
609192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
609292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
609392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
60942d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
60952d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
60965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
60975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
60985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
609992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
61005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_PROFILE)) {
61015d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            // We should already be in a profile database context, so just look up a single contact.
61025d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro           contactId = lookupSingleContactId(db);
61035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
61045d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
610592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
610692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
610792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
610892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
610992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
611092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
611192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
611292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
611392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
611492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
611592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
611692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
611792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
611892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
611992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
612092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
612192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
612292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
61235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
61245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
61275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
61285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    private long lookupSingleContactId(SQLiteDatabase db) {
61305d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        Cursor c = db.query(Tables.CONTACTS, new String[] {Contacts._ID},
61315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                null, null, null, null, null, "1");
61325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        try {
61335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (c.moveToFirst()) {
61345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return c.getLong(0);
61355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            } else {
61365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return -1;
61375d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
61385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } finally {
61395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            c.close();
61405d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
61415d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
61425d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
61435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
614443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
61455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
61475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
614843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
61495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
61505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
61515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
61525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
615443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
61555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
61565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
61575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
61585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
61605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
61615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
61625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
61635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
61645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
616592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
61665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
61675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
61685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
61695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
61715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
61725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
61745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
61755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
61765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
617743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
617843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
61795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
61805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
618143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
61825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
61835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
61845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
618592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
618692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
61875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
61885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
61895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
61905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
61915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
61925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
61935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
61945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
61955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
61965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
61975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
61985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
61995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
620092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
620143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        String TABLE = Views.RAW_CONTACTS;
62025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
62045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
620543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
62065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
620792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
62085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
62095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
621143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
62125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
621392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
62145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
62155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
621692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
621792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
621892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
621992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
62205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
62215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
622292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
622392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
622492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
62255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
622792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
622892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
62295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
623092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
623192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
623292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
623392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
623443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet = c.getString(
623543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        LookupByRawContactIdQuery.ACCOUNT_TYPE_AND_DATA_SET);
623692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
623792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
623843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
623992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
624092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
624192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
624292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
624392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
624492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
624592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
624692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
624792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
624892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
624992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
625092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
625192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
62525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
625492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
625592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
625692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
625792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
625892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
625992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
626092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
626192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
626243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
626392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
626492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
626592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
626692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
626792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
626843368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        int ACCOUNT_TYPE_AND_DATA_SET = 1;
626992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
627092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
627192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
627292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
627392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
627492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
62755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
62765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
62775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
62785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
627992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
628092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
62815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
62825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
62835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
62845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
62855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
62865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
62875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
62885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
62895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
62905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
62915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
62925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
629343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                String accountTypeAndDataSet =
629443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET);
62955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
62965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
629743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName);
62985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
62995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
63005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
630192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
630292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
630392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
63045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
63055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
63065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
63075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
63085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
63095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
63105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
63115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
63125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
63155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
63165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
631792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
631892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
631992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
632092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
632192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
632292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
632392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
632492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
632592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
632692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
632792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
6328ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
63295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        mAggregator.get().updateLookupKeyForRawContact(db, rawContactId);
6330ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
6331ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
63325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
63335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
63345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
63355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
63365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
63375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
63395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
63405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
63425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
63435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
63445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
63455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
63465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
63475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
63485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
63495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
63505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
63515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
63525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
63535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
63545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
63555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
63565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
63575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
63585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
63595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
63615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
63625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
63635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
63645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
63655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
63665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
6367763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
6368763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
63694928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        setTablesAndProjectionMapForContacts(qb, uri, projection, false);
63702f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    }
63712f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
63722f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    /**
63734928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * @param includeDataUsageStat true when the table should include DataUsageStat table.
63744928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * Note that this uses INNER JOIN instead of LEFT OUTER JOIN, so some of data in Contacts
63754928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa     * may be dropped.
63762f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa     */
63772f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
63784928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa            String[] projection, boolean includeDataUsageStat) {
637982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6380ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
63812f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
63822f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        // Just for frequently contacted contacts in Strequent Uri handling.
63834928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa        if (includeDataUsageStat) {
63842f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa            sb.append(" INNER JOIN " +
6385ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                    Views.DATA_USAGE_STAT + " AS " + Tables.DATA_USAGE_STAT +
63862f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    " ON (" +
63872f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    DbQueryUtils.concatenateClauses(
63882f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                            DataUsageStatColumns.CONCRETE_TIMES_USED + " > 0",
63894928b8c8c7a49ec088884cd9d330eeecc811dca9Daisuke Miyakawa                            RawContacts.CONTACT_ID + "=" + Views.CONTACTS + "." + Contacts._ID) +
63902f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa                    ")");
63912f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa        }
63922f830d3bb66f780937203e9738e046841a070e73Daisuke Miyakawa
63937ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
63947ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6395916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
6396916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
6397916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
6398916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6399916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
6400916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
6401916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
6402916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
6403916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
6404b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            String[] projection, String filter, long directoryId, boolean deferredSnippeting) {
64057ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov
64067ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6407ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.CONTACTS);
6408916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
640903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        if (filter != null) {
641003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            filter = filter.trim();
641103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        }
641203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
641330cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov        if (TextUtils.isEmpty(filter) || (directoryId != -1 && directoryId != Directory.DEFAULT)) {
641430cc766756461da8d53933f88ea01dd2272a90ebDmitri Plotnikov            sb.append(" JOIN (SELECT NULL AS " + SearchSnippetColumns.SNIPPET + " WHERE 0)");
64155e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        } else {
6416b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, uri, projection, filter, deferredSnippeting);
64175e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
64187ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
64197ba290f5de7f116ec0eaac30980ffef2878d2b64Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
642003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setTables(sb.toString());
642103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
642203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    }
6423916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
642403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov    private void appendSearchIndexJoin(
6425b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            StringBuilder sb, Uri uri, String[] projection, String filter,
6426b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            boolean  deferredSnippeting) {
6427916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
6428b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        if (snippetNeeded(projection)) {
642903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String[] args = null;
643003197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            String snippetArgs =
643103197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                    getQueryParameter(uri, SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY);
643203197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            if (snippetArgs != null) {
643303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov                args = snippetArgs.split(",");
643403197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
643503197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
64365e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String startMatch = args != null && args.length > 0 ? args[0]
64375e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_START_MATCH;
64385e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String endMatch = args != null && args.length > 1 ? args[1]
64395e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_END_MATCH;
64405e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            String ellipsis = args != null && args.length > 2 ? args[2]
64415e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_ELLIPSIS;
64425e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            int maxTokens = args != null && args.length > 3 ? Integer.parseInt(args[3])
64435e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                    : DEFAULT_SNIPPET_ARG_MAX_TOKENS;
64445e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6445174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            appendSearchIndexJoin(
6446b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    sb, filter, true, startMatch, endMatch, ellipsis, maxTokens,
6447b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    deferredSnippeting);
6448174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6449b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            appendSearchIndexJoin(sb, filter, false, null, null, null, 0, false);
6450174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6451174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    }
6452174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6453174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov    public void appendSearchIndexJoin(StringBuilder sb, String filter,
6454174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            boolean snippetNeeded, String startMatch, String endMatch, String ellipsis,
6455b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            int maxTokens, boolean deferredSnippeting) {
6456174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isEmailAddress = false;
6457174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String emailAddress = null;
6458174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        boolean isPhoneNumber = false;
6459174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String phoneNumber = null;
6460174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        String numberE164 = null;
6461174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
64623716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // If the query consists of a single word, we can do snippetizing after-the-fact for a
64633716f1447ceb21180d1301790eabd8b9453f486dDave Santoro        // performance boost.
6464b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        boolean singleTokenSearch = isSingleWordQuery(filter);
64653716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6466174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (filter.indexOf('@') != -1) {
64675d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            emailAddress = mDbHelper.get().extractAddressFromEmailAddress(filter);
6468174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isEmailAddress = !TextUtils.isEmpty(emailAddress);
6469174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        } else {
6470174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov            isPhoneNumber = isPhoneNumber(filter);
647104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            if (isPhoneNumber) {
647204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                phoneNumber = PhoneNumberUtils.normalizeNumber(filter);
647304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                numberE164 = PhoneNumberUtils.formatNumberToE164(phoneNumber,
64745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mDbHelper.get().getCountryIso());
647504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann            }
6476174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        }
6477174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov
6478174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        sb.append(" JOIN (SELECT " + SearchIndexColumns.CONTACT_ID + " AS snippet_contact_id");
6479174f7d319b987aa2aeeb6f2563f4b939acb8d791Dmitri Plotnikov        if (snippetNeeded) {
64805e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(", ");
64815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            if (isEmailAddress) {
64823d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
64835e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
648404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Email.ADDRESS + ")");
648504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS);
648604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
648704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID + " AND " + Email.ADDRESS + " LIKE ");
648804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                DatabaseUtils.appendEscapedSQLString(sb, filter + "%");
648904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
64903d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
64913d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(",");
64923716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6493b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                // Optimization for single-token search (do only if requested).
6494b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                if (singleTokenSearch && deferredSnippeting) {
64953716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
64963716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
64973716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
64983716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
64993d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append(")");
65003d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov            } else if (isPhoneNumber) {
65013d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                sb.append("ifnull(");
65023d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, startMatch);
650304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("||(SELECT MIN(" + Phone.NUMBER + ")");
650404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" FROM " +
650504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                        Tables.DATA_JOIN_RAW_CONTACTS + " JOIN " + Tables.PHONE_LOOKUP);
650604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" ON " + DataColumns.CONCRETE_ID);
650704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID);
650804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" WHERE  " + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
650904f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("=" + RawContacts.CONTACT_ID);
651004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(" AND " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
651104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(phoneNumber);
651204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append("%'");
651304f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(numberE164)) {
651404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
651504f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append(numberE164);
651604f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("%'");
651704f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
651804f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                sb.append(")||");
65195e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, endMatch);
65205e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(",");
65213716f1447ceb21180d1301790eabd8b9453f486dDave Santoro
6522b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                // Optimization for single-token search (do only if requested).
6523b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                if (singleTokenSearch && deferredSnippeting) {
65243716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    sb.append(SearchIndexColumns.CONTENT);
65253716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                } else {
65263716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
65273716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                }
65285e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov                sb.append(")");
652903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            } else {
653004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                final String normalizedFilter = NameNormalizer.normalize(filter);
653104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                if (!TextUtils.isEmpty(normalizedFilter)) {
6532b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    // Optimization for single-token search (do only if requested)..
6533b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson                    if (singleTokenSearch && deferredSnippeting) {
65343716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(SearchIndexColumns.CONTENT);
65353716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    } else {
65363716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("(CASE WHEN EXISTS (SELECT 1 FROM ");
65373716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.RAW_CONTACTS + " AS rc INNER JOIN ");
65383716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.NAME_LOOKUP + " AS nl ON (rc." + RawContacts._ID);
65393716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=nl." + NameLookupColumns.RAW_CONTACT_ID);
65403716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") WHERE nl." + NameLookupColumns.NORMALIZED_NAME);
65413716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" GLOB '" + normalizedFilter + "*' AND ");
65423716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("nl." + NameLookupColumns.NAME_TYPE + "=");
65433716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(NameLookupType.NAME_COLLATION_KEY + " AND ");
65443716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID);
65453716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append("=rc." + RawContacts.CONTACT_ID);
65463716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(") THEN NULL ELSE ");
65473716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        appendSnippetFunction(sb, startMatch, endMatch, ellipsis, maxTokens);
65483716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                        sb.append(" END)");
65493716f1447ceb21180d1301790eabd8b9453f486dDave Santoro                    }
655004f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                } else {
655104f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    sb.append("NULL");
655204f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                }
655303197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov            }
65545e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            sb.append(" AS " + SearchSnippetColumns.SNIPPET);
65555e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        }
655603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov
65575e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" FROM " + Tables.SEARCH_INDEX);
65585e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(" WHERE ");
65595e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(Tables.SEARCH_INDEX + " MATCH ");
65605e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        if (isEmailAddress) {
65612352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, "\"" + sanitizeMatch(filter) + "*\"");
65623d0f0e0a1325ae306842b3ad1487d3507df0821dDmitri Plotnikov        } else if (isPhoneNumber) {
65632352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb,
656404f7fc0bddad198108d2f45cb730b9506e81dedbDaniel Lehmann                    "\"" + sanitizeMatch(filter) + "*\" OR \"" + phoneNumber + "*\""
65652352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov                            + (numberE164 != null ? " OR \"" + numberE164 + "\"" : ""));
656603197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        } else {
65672352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov            DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filter) + "*");
65689c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
656903197a00e17386aa9b1971bde3cda034bc17e0c3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
6570a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov    }
6571a1e177389debb74a51587720464a527a193bffc1Dmitri Plotnikov
65722352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    private String sanitizeMatch(String filter) {
65732352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        // TODO more robust preprocessing of match expressions
65742352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov        return filter.replace('-', ' ').replace('\"', ' ');
65752352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov    }
65762352cf62c46e1caaad64c7b3dbcc601951018eb3Dmitri Plotnikov
65775e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    private void appendSnippetFunction(
65785e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov            StringBuilder sb, String startMatch, String endMatch, String ellipsis, int maxTokens) {
65795e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append("snippet(" + Tables.SEARCH_INDEX + ",");
65805e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, startMatch);
65815e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
65825e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, endMatch);
65835e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",");
65845e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        DatabaseUtils.appendEscapedSQLString(sb, ellipsis);
65855e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
65865e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        // The index of the column used for the snippet, "content"
65875e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(",1,");
65885e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(maxTokens);
65895e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov        sb.append(")");
65905e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov    }
65915e70c0772ef2b975c95787112ac0f15047a4ff0cDmitri Plotnikov
6592763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
6593763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
6594ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.RAW_CONTACTS);
6595763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
6596763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
6597f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6598763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
6599763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
6600a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
6601ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        qb.setTables(Views.RAW_ENTITIES);
6602a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
6603f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
660446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
660546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
660682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
660782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
660846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
660946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
661046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
661146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
661246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
661346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * type.
661446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
661546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
661646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            String[] projection, boolean distinct, Integer usageType) {
661782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6618ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
661982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
662082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
6621a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
6622a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6623a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6624a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
66253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
662646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (usageType != null) {
662746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
662846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
662946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
663082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
6631f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
6632f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
66335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                || !mDbHelper.get().isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
6634f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
6635f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
6636f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6637ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
6638ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
66390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
66400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
66410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
6642ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.DATA);
66430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
6644a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
6645a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
66460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6647a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6648a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
6649a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6650a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
66513b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItems(SQLiteQueryBuilder qb) {
66529b002837367674b7403769f52dc50ab4dbecef71Daniel Lehmann        qb.setTables(Views.STREAM_ITEMS);
66533b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemsProjectionMap);
66543b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
66553b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
66563b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    private void setTablesAndProjectionMapForStreamItemPhotos(SQLiteQueryBuilder qb) {
66571dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro        qb.setTables(Tables.PHOTO_FILES
66581dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + " JOIN " + Tables.STREAM_ITEM_PHOTOS + " ON ("
66591dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_PHOTO_FILE_ID + "="
66601dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + PhotoFilesColumns.CONCRETE_ID
66611dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + ") JOIN " + Tables.STREAM_ITEMS + " ON ("
66621dfa964f2e1756e27b36f99421bd403c84ea0a5fDave Santoro                + StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "="
66630bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_ID + ")"
66640bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + " JOIN " + Tables.RAW_CONTACTS + " ON ("
66650bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + StreamItemsColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
66660bf6b318e3c994294d4a885f57906debd4a0e64eDaniel Lehmann                + ")");
66673b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann        qb.setProjectionMap(sStreamItemPhotosProjectionMap);
66683b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann    }
66693b505de6c622d20d40b85b361c1437a89aef82deDaniel Lehmann
6670a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
6671a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
6672a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
6673ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann        sb.append(Views.ENTITIES);
6674a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
6675a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6676a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
6677a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
6678a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
6679a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
6680a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6681a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
6682a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
6683f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro        appendAccountFromParameter(qb, uri);
6684a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6685a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6686a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
6687a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
66885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6689a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
6690a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
6691a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
6692a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
6693a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
6694a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
6695a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
6696a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
6697a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
66980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6699a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
67000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
6701a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
6702a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
67035d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
67040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
67050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
67060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
67070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
67080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
67090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
6710a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
6711a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
67120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
6713a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6714a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
671546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
671646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
671746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
671846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
671946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
672046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
6721a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
6722a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
67235d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection,
6724a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
6725a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
6726a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
6727a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
6728a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6729a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6730a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
6731a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
6732a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
67335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mDbHelper.get().isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
6734a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
6735a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
6736a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
6737a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
6738a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
673924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    private boolean appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
6740385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
6741385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
674224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
6743385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
6744385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
674524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            return true;
674624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        }
674724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro        return false;
674824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro    }
674924c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro
6750f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
6751f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6752f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
675343368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6754e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6755e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6756e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6757e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
67585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6759fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6760e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6761e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6762e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6763e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6764e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6765e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
676643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            String toAppend = RawContacts.ACCOUNT_NAME + "="
67674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
67684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
676943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    + DatabaseUtils.sqlEscapeString(accountType);
6770f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (dataSet == null) {
6771f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + " IS NULL";
6772f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
6773f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                toAppend += " AND " + RawContacts.DATA_SET + "=" +
6774f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                        DatabaseUtils.sqlEscapeString(dataSet);
677543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
677643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            qb.appendWhere(toAppend);
67774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
67784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
67794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
67804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
67814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
6782e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
6783f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
6784f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
678543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET);
6786e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6787e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
6788e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
6789e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
67905d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6791fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
6792e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
6793e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
6794e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
6795e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
6796e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
6797e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
6798e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
6799e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
6800e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
6801e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
6802f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            if (dataSet == null) {
6803f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + " IS NULL");
6804f9b77edaf5855bf6932fbc4b4b4342273669efefDave Santoro            } else {
680543368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                selectionSb.append(" AND " + RawContacts.DATA_SET + "=")
680643368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        .append(DatabaseUtils.sqlEscapeString(dataSet));
680743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            }
6808e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
6809e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
6810e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
6811e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
6812e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
6813e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
6814e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
6815e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
6816e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
6817e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
6818e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
68197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
6820c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
6821c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
6822c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
6823c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
6824c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
6825f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
68262e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
6827c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
6828c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6829c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6830c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
6831c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
6832c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
6833c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
6834c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
6835c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
6836c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
6837c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
6838c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
6839c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
6840c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
6841c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
6842c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6843c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
6844b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
6845f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
6846f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (mode.equals("r")) {
6847f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mReadAccessLatch);
6848f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
6849f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            waitForAccess(mWriteAccessLatch);
6850f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
68515d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mapsToProfileDb(uri)) {
68525d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToProfileMode();
68535d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return mProfileProvider.openAssetFile(uri, mode);
68545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        } else {
68555d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
68565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            return openAssetFileLocal(uri, mode);
68575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
68585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    }
68595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
68605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro    public AssetFileDescriptor openAssetFileLocal(Uri uri, String mode)
68615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throws FileNotFoundException {
68625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro
68635d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        // Default active DB to the contacts DB if none has been set.
68645d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        if (mActiveDb.get() == null) {
6865078f588cef389358adabc579de00747878f3c108Dave Santoro            if (mode.equals("r")) {
6866078f588cef389358adabc579de00747878f3c108Dave Santoro                mActiveDb.set(mContactsHelper.getReadableDatabase());
6867078f588cef389358adabc579de00747878f3c108Dave Santoro            } else {
6868078f588cef389358adabc579de00747878f3c108Dave Santoro                mActiveDb.set(mContactsHelper.getWritableDatabase());
6869078f588cef389358adabc579de00747878f3c108Dave Santoro            }
68705d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        }
6871415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
6872b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
6873b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
6874a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
6875bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
68765d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
687724c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " +
687824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                                RawContacts.CONTACT_ID + "=?",
6879bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        new String[]{String.valueOf(contactId)});
6880e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
6881b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
6882f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO: {
6883f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6884f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6885f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by contact ID can only be read.");
6886f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6887f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long contactId = Long.parseLong(uri.getPathSegments().get(1));
68885d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
6889f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{Contacts.PHOTO_FILE_ID},
6890f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Contacts._ID + "=?", new String[]{String.valueOf(contactId)},
6891f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, null);
6892f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
689385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    if (c.moveToFirst()) {
689485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        long photoFileId = c.getLong(0);
689585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        return openDisplayPhotoForRead(photoFileId);
689685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    } else {
689785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        // No contact for this ID.
689885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        throw new FileNotFoundException(uri.toString());
689985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    }
690085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                } finally {
690185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    c.close();
690285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                }
690385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            }
690485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro
690585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro            case PROFILE_DISPLAY_PHOTO: {
690685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                if (!mode.equals("r")) {
690785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    throw new IllegalArgumentException(
690885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                            "Display photos retrieved by contact ID can only be read.");
690985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                }
691085077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                Cursor c = mActiveDb.get().query(Tables.CONTACTS,
691185077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        new String[]{Contacts.PHOTO_FILE_ID}, null, null, null, null, null);
691285077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                try {
691385077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    if (c.moveToFirst()) {
691485077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        long photoFileId = c.getLong(0);
691585077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        return openDisplayPhotoForRead(photoFileId);
691685077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    } else {
691785077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        // No profile record.
691885077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                        throw new FileNotFoundException(uri.toString());
691985077339f2e0c6f21fd92fb8df335f3aae004fbaDave Santoro                    }
6920f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6921f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6922f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6923f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6924f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6925bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
6926bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
6927f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
6928f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO: {
6929f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
6930f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
6931bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            "Photos retrieved by contact lookup key can only be read.");
6932f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6933f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                List<String> pathSegments = uri.getPathSegments();
6934f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                int segmentCount = pathSegments.size();
6935f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount < 4) {
69365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    throw new IllegalArgumentException(mDbHelper.get().exceptionMessage(
6937f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Missing a lookup key", uri));
6938f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6939bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro
6940bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                boolean forDisplayPhoto = (match == CONTACTS_LOOKUP_ID_DISPLAY_PHOTO
6941bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        || match == CONTACTS_LOOKUP_DISPLAY_PHOTO);
6942f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String lookupKey = pathSegments.get(2);
6943bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                String[] projection = new String[]{Contacts.PHOTO_ID, Contacts.PHOTO_FILE_ID};
6944f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (segmentCount == 5) {
6945f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    long contactId = Long.parseLong(pathSegments.get(3));
6946f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
6947f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
69485d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, mActiveDb.get(), uri,
6949f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            projection, null, null, null, null, null,
6950f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
6951f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c != null) {
6952f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        try {
6953f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.moveToFirst();
6954bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            if (forDisplayPhoto) {
6955bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoFileId =
6956bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6957bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openDisplayPhotoForRead(photoFileId);
6958bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            } else {
6959bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
6960bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
6961bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                        Data._ID + "=?", new String[]{String.valueOf(photoId)});
6962bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                            }
6963f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        } finally {
6964f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            c.close();
6965f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
6966f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
6967f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6968f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6969f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6970f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForContacts(qb, uri, projection);
69715d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
69725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection, Contacts._ID + "=?",
6973f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        new String[]{String.valueOf(contactId)}, null, null, null);
6974f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
6975f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.moveToFirst();
6976bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    if (forDisplayPhoto) {
6977bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoFileId = c.getLong(c.getColumnIndex(Contacts.PHOTO_FILE_ID));
6978bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openDisplayPhotoForRead(photoFileId);
6979bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    } else {
6980bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        long photoId = c.getLong(c.getColumnIndex(Contacts.PHOTO_ID));
6981bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                        return openPhotoAssetFile(mActiveDb.get(), uri, mode,
6982bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                                Data._ID + "=?", new String[]{String.valueOf(photoId)});
6983bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro                    }
6984f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
6985f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
6986f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
6987f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
6988f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6989f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO: {
6990f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
6991f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                boolean writeable = !mode.equals("r");
6992f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
6993f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Find the primary photo data record for this raw contact.
6994f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6995f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                String[] projection = new String[]{Data._ID, Photo.PHOTO_FILE_ID};
6996f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                setTablesAndProjectionMapForData(qb, uri, projection, false);
69977cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
69985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                Cursor c = qb.query(mActiveDb.get(), projection,
69997cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data.RAW_CONTACT_ID + "=? AND " + DataColumns.MIMETYPE_ID + "=?",
70007cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        new String[]{String.valueOf(rawContactId), String.valueOf(photoMimetypeId)},
7001f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        null, null, Data.IS_PRIMARY + " DESC");
7002f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long dataId = 0;
7003f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = 0;
7004f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                try {
7005f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (c.getCount() >= 1) {
7006f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        c.moveToFirst();
7007f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        dataId = c.getLong(0);
7008f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        photoFileId = c.getLong(1);
7009f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7010f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } finally {
7011f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    c.close();
7012f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7013f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7014f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // If writeable, open a writeable file descriptor that we can monitor.
7015f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // When the caller finishes writing content, we'll process the photo and
7016f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // update the data record.
7017f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (writeable) {
7018f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForWrite(rawContactId, dataId, uri, mode);
7019f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                } else {
7020f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    return openDisplayPhotoForRead(photoFileId);
7021f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7022f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7023f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7024f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO: {
7025f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long photoFileId = ContentUris.parseId(uri);
7026f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (!mode.equals("r")) {
7027f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    throw new IllegalArgumentException(
7028f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            "Display photos retrieved by key can only be read.");
7029f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7030f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return openDisplayPhotoForRead(photoFileId);
7031f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7032f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7033e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
703424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                long dataId = Long.parseLong(uri.getPathSegments().get(1));
70357cf50494501938f175d288077145acf49da8f171Daniel Lehmann                long photoMimetypeId = mDbHelper.get().getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
70365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                return openPhotoAssetFile(mActiveDb.get(), uri, mode,
70377cf50494501938f175d288077145acf49da8f171Daniel Lehmann                        Data._ID + "=? AND " + DataColumns.MIMETYPE_ID + "=" + photoMimetypeId,
703824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                        new String[]{String.valueOf(dataId)});
7039d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7040d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7041fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case PROFILE_AS_VCARD: {
7042fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // When opening a contact as file, we pass back contents as a
7043fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // vCard-encoded stream. We build into a local buffer first,
7044fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                // then pipe into MemoryFile once the exact size is known.
7045fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7046fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
7047fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                return buildAssetFileDescriptor(localStream);
7048fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            }
704942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
7050fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            case CONTACTS_AS_VCARD: {
705142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
705242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
705342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
705442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7055fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(uri, localStream, null, null);
7056f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
705742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
705842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
705942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
706042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
706142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
706242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
7063fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Uri queryUri = Contacts.CONTENT_URI;
706442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
7065fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen
7066d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
7067d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
706842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
706942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
7070d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
707142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
7072d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
707342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
70745d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    // TODO: Figure out what to do if the profile contact is in the list.
70755d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long contactId = lookupContactIdByLookupKey(mActiveDb.get(), lookupKey);
707624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro                    inBuilder.append(contactId);
707742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
707842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
707942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
708042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
7081d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7082d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
7083d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
7084d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
7085d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
7086fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                outputRawContactsAsVCard(queryUri, localStream, selection, null);
7087f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
7088d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7089b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7090b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
70915d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                throw new FileNotFoundException(mDbHelper.get().exceptionMessage(
70925d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        "File does not exist", uri));
7093b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
7094b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
7095b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
7096afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro    private AssetFileDescriptor openPhotoAssetFile(SQLiteDatabase db, Uri uri, String mode,
7097afc8e7ad6e5208db6c87b8500ecc1246ad966d62Dave Santoro            String selection, String[] selectionArgs)
7098e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
7099e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
71005d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            throw new FileNotFoundException(mDbHelper.get().exceptionMessage("Mode " + mode
7101e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
7102e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
7103e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7104e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
7105ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                "SELECT " + Photo.PHOTO + " FROM " + Views.DATA +
7106e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
710708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
7108f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7109f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
711008ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
711108ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
711208ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
711308ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
7114e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
7115e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
7116f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7117f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a display photo from the photo store for reading.
7118f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoFileId The display photo file ID
7119f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor that allows the file to be read.
7120f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @throws FileNotFoundException If no photo file for the given ID exists.
7121f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7122f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForRead(long photoFileId)
7123f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throws FileNotFoundException {
71245d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        PhotoStore.Entry entry = mPhotoStore.get().get(photoFileId);
7125f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
7126d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            try {
7127d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                return makeAssetFileDescriptor(
7128d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        ParcelFileDescriptor.open(new File(entry.path),
7129d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                                ParcelFileDescriptor.MODE_READ_ONLY),
7130d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                        entry.size);
7131d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            } catch (FileNotFoundException fnfe) {
7132d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
7133d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro                throw fnfe;
7134d9125effce84804631c8e618ae88b2cfc69cf529Dave Santoro            }
7135f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
7136f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
7137f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            throw new FileNotFoundException("No photo file found for ID " + photoFileId);
7138f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7139f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7140f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7141f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7142f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Opens a file descriptor for a photo to be written.  When the caller completes writing
7143f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * to the file (closing the output stream), the image will be parsed out and processed.
7144f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If processing succeeds, the given raw contact ID's primary photo record will be
7145f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * populated with the inserted image (if no primary photo record exists, the data ID can
7146f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * be left as 0, and a new data record will be inserted).
7147f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rawContactId Raw contact ID this photo entry should be associated with.
7148f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param dataId Data ID for a photo mimetype that will be updated with the inserted
7149f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     image.  May be set to 0, in which case the inserted image will trigger creation
7150f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     of a new primary photo image data row for the raw contact.
7151f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param uri The URI being used to access this file.
7152f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param mode Read/write mode string.
7153f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return An asset file descriptor the caller can use to write an image file for the
7154f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     raw contact.
7155f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7156f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private AssetFileDescriptor openDisplayPhotoForWrite(long rawContactId, long dataId, Uri uri,
7157f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            String mode) {
7158f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        try {
7159c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            ParcelFileDescriptor[] pipeFds = ParcelFileDescriptor.createPipe();
7160c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            PipeMonitor pipeMonitor = new PipeMonitor(rawContactId, dataId, pipeFds[0]);
7161c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            pipeMonitor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) null);
7162c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return new AssetFileDescriptor(pipeFds[1], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
7163f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } catch (IOException ioe) {
7164f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.e(TAG, "Could not create temp image file in mode " + mode);
7165f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return null;
7166f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7167f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7168f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7169f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
7170c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * Async task that monitors the given file descriptor (the read end of a pipe) for
7171c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * the writer finishing.  If the data from the pipe contains a valid image, the image
7172c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro     * is either inserted into the given raw contact or updated in the given data row.
7173f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
7174c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro    private class PipeMonitor extends AsyncTask<Object, Object, Object> {
7175c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private final ParcelFileDescriptor mDescriptor;
7176f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mRawContactId;
7177f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        private final long mDataId;
7178c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        private PipeMonitor(long rawContactId, long dataId, ParcelFileDescriptor descriptor) {
7179f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mRawContactId = rawContactId;
7180f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mDataId = dataId;
7181c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            mDescriptor = descriptor;
7182f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7183f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7184f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        @Override
7185c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro        protected Object doInBackground(Object... params) {
7186c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            AutoCloseInputStream is = new AutoCloseInputStream(mDescriptor);
7187f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
7188c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                Bitmap b = BitmapFactory.decodeStream(is);
7189f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (b != null) {
7190fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann                    waitForAccess(mWriteAccessLatch);
7191f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    PhotoProcessor processor = new PhotoProcessor(b, mMaxDisplayPhotoDim,
7192f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            mMaxThumbnailPhotoDim);
7193f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7194f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Store the compressed photo in the photo store.
71955d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    PhotoStore photoStore = ContactsContract.isProfileId(mRawContactId)
71965d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            ? mProfilePhotoStore
71975d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            : mContactsPhotoStore;
71985d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    long photoFileId = photoStore.insert(processor);
7199f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7200c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // Depending on whether we already had a data row to attach the photo
7201c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                    // to, do an update or insert.
7202f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (mDataId != 0) {
7203f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Update the data record with the new photo.
7204f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues updateValues = new ContentValues();
7205f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7206f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7207f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7208f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7209f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7210f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            updateValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7211f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7212f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        updateValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7213c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                        update(ContentUris.withAppendedId(Data.CONTENT_URI, mDataId),
7214c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                                updateValues, null, null);
7215f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    } else {
7216f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Insert a new primary data record with the photo.
7217f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        ContentValues insertValues = new ContentValues();
7218f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7219f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        // Signal that photo processing has already been handled.
7220f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7221f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7222f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7223f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Data.IS_PRIMARY, 1);
7224f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        if (photoFileId != 0) {
7225f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                            insertValues.put(Photo.PHOTO_FILE_ID, photoFileId);
7226f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        }
7227f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insertValues.put(Photo.PHOTO, processor.getThumbnailPhotoBytes());
7228f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        insert(RawContacts.CONTENT_URI.buildUpon()
7229f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(String.valueOf(mRawContactId))
7230f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                .appendPath(RawContacts.Data.CONTENT_DIRECTORY).build(),
7231f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                                insertValues);
7232f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
7233c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro
7234f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
7235c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            } catch (IOException e) {
7236c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro                throw new RuntimeException(e);
7237f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
7238c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro            return null;
7239f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
7240f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
7241f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
7242d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
7243d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7244d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7245f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
7246d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
7247d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7248f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
7249d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
7250d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
7251d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7252d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
7253d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7254f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
7255f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
7256f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
7257d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
7258ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
7259ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
7260d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7261d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7262d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7263f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
7264f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
7265f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7266f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7267f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
7268f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
7269f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
7270f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
7271d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
7272d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
7273d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
7274d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
7275d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
7276fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen    private void outputRawContactsAsVCard(Uri uri, OutputStream stream,
7277fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen            String selection, String[] selectionArgs) {
7278d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
7279dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        int vcardconfig = VCardConfig.VCARD_TYPE_DEFAULT;
7280fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen        if(uri.getBooleanQueryParameter(
7281fdd04bc3a972cd72dfe7bf925e1624d656b34cf7Martijn Coenen                Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, false)) {
7282dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen            vcardconfig |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
7283dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen        }
72847a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
7285dfa6d58328345c7c91f2467d29189a57b96bfe2aMartijn Coenen                new VCardComposer(context, vcardconfig, false);
7286108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        Writer writer = null;
72873711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        final Uri rawContactsUri;
72883711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        if (mapsToProfileDb(uri)) {
728982792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // Pre-authorize the URI, since the caller would have already gone through the
729082792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // permission check to get here, but the pre-authorization at the top level wouldn't
729182792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            // carry over to the raw contact.
729282792ae937085bfa1f7878166e89ca4ea84fd652Dave Santoro            rawContactsUri = preAuthorizeUri(RawContactsEntity.PROFILE_CONTENT_URI);
72933711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        } else {
72943711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            rawContactsUri = RawContactsEntity.CONTENT_URI;
72953711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen        }
7296108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        try {
7297108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            writer = new BufferedWriter(new OutputStreamWriter(stream));
72983711af1a5799a7ae0c8e761e13a67a9fb5878cc8Martijn Coenen            if (!composer.init(uri, selection, selectionArgs, null, rawContactsUri)) {
7299108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                Log.w(TAG, "Failed to init VCardComposer");
7300108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                return;
7301108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7302d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
7303108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            while (!composer.isAfterLast()) {
7304108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                writer.write(composer.createOneEntry());
7305108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            }
7306108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } catch (IOException e) {
7307108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            Log.e(TAG, "IOException: " + e);
7308108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa        } finally {
7309108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            composer.terminate();
7310108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa            if (writer != null) {
7311108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                try {
7312108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    writer.close();
7313108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                } catch (IOException e) {
7314108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                    Log.w(TAG, "IOException during closing output stream: " + e);
7315108f1be6b0e855f1b335bc591755a9e5f488175aDaisuke Miyakawa                }
7316d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
7317d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
7318d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
7319b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
73204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
73214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
7322415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7323415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov        waitForAccess(mReadAccessLatch);
7324415ba9ec45dc1be35d3921b28e1dae23e150fb25Dmitri Plotnikov
7325a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
73264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
7327b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
7328be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
73292d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
7330b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
7331b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
733224c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
7333b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
7334f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
733542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
733624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
7337f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
7338f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
7339bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_PHOTO:
7340bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            case CONTACTS_LOOKUP_ID_PHOTO:
7341f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_ID_DISPLAY_PHOTO:
7342f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_DISPLAY_PHOTO:
7343f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case CONTACTS_LOOKUP_ID_DISPLAY_PHOTO:
7344f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case RAW_CONTACTS_ID_DISPLAY_PHOTO:
7345f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            case DISPLAY_PHOTO:
7346f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                return "image/jpeg";
7347b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
734824c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
7349be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
7350b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
735124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
7352b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
7353f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
735424c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
7355f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
7356508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
73575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                long id = ContentUris.parseId(uri);
73585d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                if (ContactsContract.isProfileId(id)) {
73595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mProfileHelper.getDataMimeType(id);
73605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                } else {
73615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    return mContactsHelper.getDataMimeType(id);
73625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                }
736348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
736448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
736548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
736648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
73679005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
73689005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
736948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
737048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
737148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
737248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
737348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
737448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
737548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
737648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
7377b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
7378b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
7379b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
7380b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
7381b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
7382b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
7383b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
7384b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
7385c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
7386c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
7387c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
7388c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
7389d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
7390d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
7391d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
7392d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
7393af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS:
7394af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_TYPE;
7395af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID:
7396af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.CONTENT_ITEM_TYPE;
7397af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS:
7398af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_TYPE;
7399af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_ID_PHOTOS_ID:
7400af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                return StreamItems.StreamItemPhotos.CONTENT_ITEM_TYPE;
7401af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki            case STREAM_ITEMS_PHOTOS:
7402af43bfb95070c234ae7090f6041f6fc62366313aMakoto Onuki                throw new UnsupportedOperationException("Not supported for write-only URI " + uri);
740361efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
740461efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
74054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
74064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
74077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
740809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
740909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
741009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
741109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
741209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
741309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
741409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
741509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
741624c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE:
741709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
741809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
74198727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
742024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_ENTITIES:
74218727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
74228727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
742309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
742409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
742524c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_AS_VCARD:
742609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
742709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
742809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
742909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
743024c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS:
743124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_RAW_CONTACTS_ID:
743209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
743309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
743409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
743509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
743609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
743709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
743809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
743909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
744009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
744124c1d384b45a6d3c1cc959062a9d4308335fabbfDave Santoro            case PROFILE_DATA:
744209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
744309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
744409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
744509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
744609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
744709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
744809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
744909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
745009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
745109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
745209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
745309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
745409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
745509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
745609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
745709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
745809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
745909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
746009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
746109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
746209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
7463f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
7464f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7465f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
7466f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
7467f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7468f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7469f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7470f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
7471f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
74725d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mDbHelper.get().insertNameLookup(rawContactId, dataId, lookupType, name);
7473f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7474f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
7475f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
7476f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
7477d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
7478f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
7479f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
7480f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
74812d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
7482d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
7483d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
7484d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
7485d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
7486d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
7487d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
7488d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
7489e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
7490916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
7491916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
7492e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
7493e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
74949a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    public boolean isPhoneNumber(String filter) {
74959a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        boolean atLeastOneDigit = false;
74969a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        int len = filter.length();
74979a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        for (int i = 0; i < len; i++) {
74989a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            char c = filter.charAt(i);
74999a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            if (c >= '0' && c <= '9') {
75009a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                atLeastOneDigit = true;
75019a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            } else if (c != '*' && c != '#' && c != '+' && c != 'N' && c != '.' && c != ';'
75029a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                    && c != '-' && c != '(' && c != ')' && c != ' ') {
75039a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov                return false;
75049a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            }
75059a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
75069a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return atLeastOneDigit;
75079a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
75089a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
75094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
75107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
75117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
75127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
75137a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
75147a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
75157a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
75167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
75177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
75187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
75197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
7520f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov        DataRowHandlerForStructuredName handler = (DataRowHandlerForStructuredName)
7521f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov                getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
75227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
75237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
75247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
75257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
75267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
75277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
75287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
75297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
75307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
75317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
75327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
75337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
75347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
75357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
75367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
75377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
75387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
75397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
75407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
75417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
75427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
75437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
75447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
75457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
75467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
75477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
75487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
75497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
75507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
75517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
75527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
75537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
75547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
75557a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
75567a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
75577a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
75584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
75594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
75604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
7561b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
7562b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
7563b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
7564b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
7565b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
75664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
75674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
7568b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
7569b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
7570b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
7571caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
75725e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
75735e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
75745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
75755e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
75765e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
75775e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
75785e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
75795e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
75805e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
75815e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
75825e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
7583caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
7584caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
7585caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
75865f1f4a062ac34d75d2dbf586702cbeb121cf09caDmitri Plotnikov            Account[] accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE);
7587caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
7588caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
7589caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
7590caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
75916f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
7592caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
75936f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
7594caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
7595f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
759673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
759743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro     * Returns true if the specified account type and data set is writable.
759873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
759943368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro    protected boolean isWritableAccountWithDataSet(String accountTypeAndDataSet) {
760043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        if (accountTypeAndDataSet == null) {
7601bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
7602bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
7603bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
760443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        Boolean writable = mAccountWritability.get(accountTypeAndDataSet);
760573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
760673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
760773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
760873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
7609627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
7610627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
761143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro            // TODO(dsantoro): Need to update this logic to allow for sub-accounts.
7612627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
7613627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
761443368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                        accountTypeAndDataSet.equals(sync.accountType)) {
761573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
761673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
7617627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
7618627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
7619627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
7620627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
7621627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
762273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
762373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
762473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
762573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
762673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
762743368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro        mAccountWritability.put(accountTypeAndDataSet, writable);
762873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
7629627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
7630b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
7631d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
7632f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
7633f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
7634f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7635f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
7636f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7637f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7638f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7639f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7640f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7641f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
7642f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
7643f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
7644f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7645f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7646f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
7647f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7648f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
7649f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
7650f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7651f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7652f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
7653f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
7654f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
7655f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
7656f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
7657f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
7658f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7659f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
7660f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
7661f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
7662f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
7663f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
7664f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
7665f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
7666f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7667f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7668f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
7669f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
7670f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7671f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
7672f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
7673f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
7674f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
7675f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
7676f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
76775fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            }
76785fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa
76795fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // Should match against the whole parameter instead of its suffix.
76805fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            // e.g. The parameter "param" must not be found in "some_param=val".
76815fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa            if (index > 0) {
76825fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                char prevChar = query.charAt(index - 1);
76835fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                if (prevChar != '?' && prevChar != '&') {
76845fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    // With "some_param=val1&param=val2", we should find second "param" occurrence.
76855fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    index += parameterLength;
76865fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                    continue;
76875fdc17bae46504edebe7285c3dbc7691ef3fbeb9Daisuke Miyakawa                }
7688f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7689f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7690f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
7691f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7692f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
7693f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
7694f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7695f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7696f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
7697f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
7698f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
7699f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
7700f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7701f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7702f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
7703f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
7704f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
7705f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
7706f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
7707f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
7708f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
7709f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
7710f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
77115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
77120dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
77130dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
77140dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
77150dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
77160dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
77175d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        int version = Integer.parseInt(mContactsHelper.getProperty(
77185d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                PROPERTY_AGGREGATION_ALGORITHM, "1"));
77190dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
77200dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
77210dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
7722bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    protected void upgradeAggregationAlgorithmInBackground() {
77230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
77240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
77250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
77260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
77270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
77280dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
77295d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        SQLiteDatabase db = null;
77300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
77315d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            switchToContactMode();
77325d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db = mContactsHelper.getWritableDatabase();
77335d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.set(db);
77345d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.beginTransaction();
77355d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            Cursor cursor = db.query(true,
77360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
77370dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
77380dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
77390dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
77400dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
774143368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE +
774243368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro                    " AND r1." + RawContacts.DATA_SET + "=r2." + RawContacts.DATA_SET,
77430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
77440dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
77450dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
77460dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
77470dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
77480dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
77490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
77500dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
77510dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
77520dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
77530dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
77545d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactAggregator.aggregateInTransaction(mTransactionContext.get(), db);
7755bd9abbb6b03b4ec1e28ad3fa2fcba5d1eb8609eaDmitri Plotnikov            updateSearchIndexInTransaction();
77565d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            db.setTransactionSuccessful();
77575d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mContactsHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
77580dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
77590dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
77605d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            if (db != null) {
77615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                db.endTransaction();
77625d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            }
77630dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
77640dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
77650dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
77660dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
77670dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
77689a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov
77699a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    /* Visible for testing */
77709a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    boolean isPhone() {
77719a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        if (!sIsPhoneInitialized) {
77729a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhone = new TelephonyManager(getContext()).isVoiceCapable();
77739a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov            sIsPhoneInitialized = true;
77749a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        }
77759a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov        return sIsPhone;
77769a6be1610fdf40c2f7f04cfe4b66fde3a35940dcDmitri Plotnikov    }
777746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
777846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private boolean handleDataUsageFeedback(Uri uri) {
777946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final long currentTimeMillis = System.currentTimeMillis();
778046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
778146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] ids = uri.getLastPathSegment().trim().split(",");
778246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ArrayList<Long> dataIds = new ArrayList<Long>();
778346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
778446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (String id : ids) {
778546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            dataIds.add(Long.valueOf(id));
778646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
778746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final boolean successful;
778846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (TextUtils.isEmpty(usageType)) {
778946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
779046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = false;
779146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
779246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
779346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
779446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
779546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Handle old API. This doesn't affect the result of this entire method.
779646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] questionMarks = new String[ids.length];
779746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        Arrays.fill(questionMarks, "?");
779846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
77995d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro        final Cursor cursor = mActiveDb.get().query(
7800ed6bfd922fd84db21de08c1d12e93c501b86560dDaniel Lehmann                Views.DATA,
780146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { Data.CONTACT_ID },
780246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                where, ids, null, null, null);
780346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        try {
780446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            while (cursor.moveToNext()) {
780546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                mSelectionArgs1[0] = cursor.getString(0);
780646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                ContentValues values2 = new ContentValues();
780746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
78085d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().update(Tables.CONTACTS, values2, Contacts._ID + "=?",
78095d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mSelectionArgs1);
78105d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
78115d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
781246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
781346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } finally {
781446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            cursor.close();
781546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
781646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
781746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return successful;
781846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
781946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
782046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
782146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Update {@link Tables#DATA_USAGE_STAT}.
782246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     *
782346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * @return the number of rows affected.
782446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
7825f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    @VisibleForTesting
7826f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa    /* package */ int updateDataUsageStat(
7827f9648a03e88e2d1a91c616a20d903e4c9a2468e5Daisuke Miyakawa            List<Long> dataIds, String type, long currentTimeMillis) {
782846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final int typeInt = sDataUsageTypeMap.get(type);
782946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String where = DataUsageStatColumns.DATA_ID + " =? AND "
783046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
783146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String[] columns =
783246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
783346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final ContentValues values = new ContentValues();
783446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        for (Long dataId : dataIds) {
783546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
78365d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro            mActiveDb.get().beginTransaction();
783746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            try {
78385d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                final Cursor cursor = mActiveDb.get().query(Tables.DATA_USAGE_STAT, columns, where,
78395d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        args, null, null, null);
784046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                try {
784146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    if (cursor.getCount() > 0) {
784246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        if (!cursor.moveToFirst()) {
784346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            Log.e(TAG,
784446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    "moveToFirst() failed while getAccount() returned non-zero.");
784546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        } else {
784646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.clear();
784746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
784846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                            values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
78495d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                            mActiveDb.get().update(Tables.DATA_USAGE_STAT, values,
785046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    DataUsageStatColumns._ID + " =?",
785146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                                    new String[] { cursor.getString(0) });
785246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        }
785346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    } else {
785446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.clear();
785546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.DATA_ID, dataId);
785646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
785746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.TIMES_USED, 1);
785846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                        values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
78595d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                        mActiveDb.get().insert(Tables.DATA_USAGE_STAT, null, values);
786046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    }
78615d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                    mActiveDb.get().setTransactionSuccessful();
786246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                } finally {
786346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                    cursor.close();
786446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                }
786546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            } finally {
78665d0a768b56ed4bd0dfef81b8389247ba74766659Dave Santoro                mActiveDb.get().endTransaction();
786746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
786846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
786946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
787046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        return dataIds.size();
787146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
787246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
787346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    /**
787446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
787546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * associated with a primary account. The primary account should be supplied from applications
787646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
787746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
787846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     * account isn't available.
787946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa     */
788046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    private String getAccountPromotionSortOrder(Uri uri) {
788146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountName =
788246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
788346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        final String primaryAccountType =
788446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
788546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa
788646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        // Data rows associated with primary account should be promoted.
788746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        if (!TextUtils.isEmpty(primaryAccountName)) {
788846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            StringBuilder sb = new StringBuilder();
788946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
789046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
789146abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            if (!TextUtils.isEmpty(primaryAccountType)) {
789246abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
789346abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa                DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
789446abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            }
789546abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            sb.append(" THEN 0 ELSE 1 END)");
789646abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return sb.toString();
789746abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        } else {
789846abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa            return null;
789946abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa        }
790046abbb56764add30cb6e6506f55d8dededc88113Daisuke Miyakawa    }
7901b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7902b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
7903b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the URI for a deferred snippeting request
7904b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a deferred snippeting request is in the RI
7905b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
7906b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean deferredSnippetingRequested(Uri uri) {
7907b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        String deferredSnippeting =
7908b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson            getQueryParameter(uri, SearchSnippetColumns.DEFERRED_SNIPPETING_KEY);
7909b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return !TextUtils.isEmpty(deferredSnippeting) &&  deferredSnippeting.equals("1");
7910b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
7911b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7912b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
7913b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks if query is a single word or not.
7914b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if the query is one word or not
7915b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
7916b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean isSingleWordQuery(String query) {
7917b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return query.split(QUERY_TOKENIZER_REGEX).length == 1;
7918b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
7919b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson
7920b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    /**
7921b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * Checks the projection for a SNIPPET column indicating that a snippet is needed
7922b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     * @return a boolean indicating if a snippet is needed or not.
7923b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson     */
7924b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    private boolean snippetNeeded(String [] projection) {
7925b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson        return mDbHelper.get().isInProjection(projection, SearchSnippetColumns.SNIPPET);
7926b3a1271feb57be104aabe8046846da0071a1f23eIsaac Katzenelson    }
79274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
7928